Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
7f9db12
Make the IActsGeoSvc return a shared_ptr
tmadlener Dec 3, 2025
fc18585
Add retrieving magnetic field to the IActsGeoSvc
tmadlener Dec 3, 2025
0832ea0
First version of service for building Gen3 geometries
tmadlener Dec 2, 2025
ab05f38
Make selection configurable and add example
tmadlener Dec 3, 2025
f9932ed
Move to builder pattern for constructing the detector
tmadlener Dec 3, 2025
9a987bd
Add algorithm to test propagation through geometry
tmadlener Dec 3, 2025
9dc1b29
Switch to new interface for IActsGeoSvc
tmadlener Dec 3, 2025
7bfa676
Make propagation through geometry work
tmadlener Dec 3, 2025
7625324
Fix order of Property
tmadlener Dec 3, 2025
7dc48bc
Store step outptus
tmadlener Dec 3, 2025
18fcd72
Make the output as root file
tmadlener Dec 3, 2025
bd9ab99
Add plotting script and make gun configurable
tmadlener Dec 3, 2025
b06a14a
Make sure to set a proper length scale for conversion
tmadlener Dec 3, 2025
4be557e
(Almost) add an endcap
tmadlener Dec 4, 2025
6b47546
Add the outer tracker barrel to do some tracking
tmadlener Dec 4, 2025
eadef36
Make geometry conversion work for full barrel
tmadlener Dec 11, 2025
e86ef67
Add first attempts of getting full InnerTracker converted
tmadlener Dec 11, 2025
bb35f27
Add the OuterTracker endcaps
tmadlener Dec 12, 2025
8c117ad
Add Vertex detector to geometry conversion
tmadlener Dec 12, 2025
808b289
Replace old Geo svc with new implementation
tmadlener Dec 19, 2025
993a618
Make things compile again after upstream changes
tmadlener Feb 27, 2026
1efd71c
Make sure to build TestPropagator
tmadlener Feb 27, 2026
4c4514f
Use newer version of VertexEncap geometry with layers
tmadlener Feb 27, 2026
e5d9bb7
Add printout of detector name
tmadlener Feb 27, 2026
12aae12
Move blueprint population into separate function
tmadlener Mar 2, 2026
56d1a10
Introduce a runtime map for population functions
tmadlener Mar 2, 2026
cc967a6
Move blueprint population into library-like construct
tmadlener Mar 4, 2026
e31e232
Fix issues after uptream API update
tmadlener Mar 4, 2026
f925e90
Make double layers for the ILD Vertex Barrel
tmadlener Mar 4, 2026
7540db6
Add rest of inner tracker for ILD at FCCee
tmadlener Mar 4, 2026
1541f4e
Set attachment strategy via builder
tmadlener Mar 4, 2026
f758e0b
Start adding FCCee_v02 model for ILD and create some utilities
tmadlener Mar 5, 2026
96d0271
Upstream API change fixes
tmadlener Mar 5, 2026
e169de8
Use new possibilities to group Layers internally
tmadlener Mar 5, 2026
59a78c5
Refactor vertex building
tmadlener Mar 5, 2026
2dda5d8
Refactor inner tracker construction for ILD_FCCee_v01
tmadlener Mar 5, 2026
d3355e5
Refactor outer tracker construction for MAIA_v0
tmadlener Mar 5, 2026
6c94bc0
Refactor inner tracker construction for MAIA_v0
tmadlener Mar 5, 2026
c9d2b7c
Finalize conversion of ILD_FCCee_v02
tmadlener Mar 6, 2026
f3f72bd
Lift out more common functionality into blueprint functions
tmadlener Mar 6, 2026
76f3123
Add license headers to new files
tmadlener Mar 6, 2026
d49d980
Interface fixes for upstream planar layer changes
tmadlener Mar 9, 2026
16f8539
Move LUXE construction into general GeoSvc
tmadlener Mar 10, 2026
c086e54
Keep things compiling
tmadlener Mar 10, 2026
80908bc
Remove algorithms and services that are out of scope
tmadlener Mar 10, 2026
0c31a3d
Remove duplicated files from merging LUXE
tmadlener Mar 17, 2026
d2399b4
Remove unnecessary file
tmadlener Apr 16, 2026
9af7765
Update tests to load geometries that should actually work
tmadlener Apr 16, 2026
ba83b06
Replace old Geo svc with new implementation
tmadlener Dec 19, 2025
957d42c
Make sure tests pick up our version
tmadlener Apr 17, 2026
491991a
Lift grouping into helper function
tmadlener Apr 20, 2026
b6cd23b
Make tests more chatty
tmadlener Apr 20, 2026
e1e6a85
Make unmodified CLD geometries convert
tmadlener Apr 20, 2026
face23f
Generalize endcap attachment function
tmadlener Apr 20, 2026
eaac365
Remove unused member
tmadlener Apr 20, 2026
583c90b
Explicitly check CellID to surface map for mismatch
tmadlener Apr 20, 2026
acb88bb
Properly stack ILD_FCCee_v01
tmadlener Apr 20, 2026
d330032
Make grouped layers unique without Acts help
tmadlener Apr 20, 2026
5054d33
Add SET to ILD_FCCee_v01 and generalize double layer barrel creation
tmadlener Apr 21, 2026
a78b955
Re-use existing options file for tests
tmadlener Apr 21, 2026
46e74ea
Refactor blueprint construction to reduce code duplication
tmadlener Apr 21, 2026
74473a2
Add docstrings to functions
tmadlener Apr 21, 2026
4053b4d
Add general introductory documentation and re-org file contents
tmadlener Apr 21, 2026
d722ab7
Make sure to create distinct steps files
tmadlener Apr 21, 2026
634b894
Generalize barrel creation to follow similar structure as the rest
tmadlener Apr 21, 2026
7ea65dd
Extract more functionality into helper functions
tmadlener Apr 21, 2026
76690b9
Fix a few details that have been lost in refactoring
tmadlener Apr 21, 2026
1fb7c0a
Make sure to run propagation checks
tmadlener Apr 21, 2026
f939537
Add TODOs for ILD_FCCee models
tmadlener Apr 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions k4ActsTracking/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ set(_plugin_sources
src/components/GeometryIdSelector.cxx
src/components/Helpers.cxx
src/components/TrackTruthAlg.cxx
src/components/ActsTestPropagator.cpp
src/components/DD4hepBlueprintConstruction.cpp
)

gaudi_add_module(k4ActsTrackingPlugins
Expand Down
67 changes: 67 additions & 0 deletions k4ActsTracking/examples/plot_steps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/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
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", 1200, 800)
# 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, 1500)

# Save the plot
canvas.SaveAs(args.output)
print(f"Plot saved as {args.output}")


if __name__ == "__main__":
main()
64 changes: 64 additions & 0 deletions k4ActsTracking/examples/test_visualize_acts_geo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/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 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()
if args.test_propagation:
iosvc.Output = f"{pathlib.Path(args.compactFile).stem}-steps.root"

geoSvc = GeoSvc()
geoSvc.detectors = [args.compactFile]

actsGeoSvc = ActsGeoSvc("ActsGeoSvc")
actsGeoSvc.DumpVisualization = True
actsGeoSvc.ObjVisFileName = f"{pathlib.Path(args.compactFile).stem}-acts-geo.obj"
actsGeoSvc.OutputLevel = VERBOSE

alg_list = []

if args.test_propagation:
propTest = ActsTestPropagator("TestPropagator")
propTest.OutputLevel = DEBUG
propTest.NumTracks = 100
alg_list.append(propTest)

ApplicationMgr(
TopAlg=alg_list,
ExtSvc=[geoSvc, actsGeoSvc, EventDataSvc()],
EvtMax=1,
EvtSel="NONE",
)
12 changes: 9 additions & 3 deletions k4ActsTracking/include/k4ActsTracking/IActsGeoSvc.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
#define IACTSGEOSVC_H

#include <GaudiKernel/IService.h>

#include <cstdint>
#include <memory>
#include <unordered_map>

namespace dd4hep {
Expand All @@ -32,18 +35,21 @@ namespace dd4hep {
namespace Acts {
class TrackingGeometry;
class Surface;
class MagneticFieldProvider;
} // namespace Acts

class GAUDI_API IActsGeoSvc : virtual public IService {
public:
using VolumeSurfaceMap = std::unordered_map<uint64_t, const Acts::Surface*>;
using CellIDSurfaceMap = std::unordered_map<uint64_t, const Acts::Surface*>;

public:
DeclareInterfaceID(IActsGeoSvc, 1, 0);

virtual const Acts::TrackingGeometry& trackingGeometry() const = 0;
virtual std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry() const = 0;
virtual std::shared_ptr<const Acts::MagneticFieldProvider> magneticField() const = 0;
virtual const CellIDSurfaceMap& cellIdToSurfaceMap() const = 0;

virtual ~IActsGeoSvc() {}
virtual ~IActsGeoSvc() = default;
};

#endif // IACTSGEOSVC_H
183 changes: 119 additions & 64 deletions k4ActsTracking/src/components/ActsGeoSvc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,90 +16,145 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "ActsGeoSvc.h"
#include <Acts/Geometry/GeometryContext.hpp>
#include "DD4hepBlueprintConstruction.h"

#include "k4ActsTracking/ActsGaudiLogger.h"

#include "k4Interface/IGeoSvc.h"
#include <k4FWCore/GaudiChecks.h>

#include <Acts/Definitions/Algebra.hpp>
#include <Acts/Definitions/Units.hpp>
#include <Acts/Geometry/Blueprint.hpp>
#include <Acts/Geometry/BlueprintNode.hpp>
#include <Acts/Geometry/BlueprintOptions.hpp>
#include <Acts/Geometry/ContainerBlueprintNode.hpp>
#include <Acts/Geometry/CylinderVolumeBounds.hpp>
#include <Acts/Geometry/Extent.hpp>
#include <Acts/Geometry/GeometryContext.hpp>
#include <Acts/Geometry/GeometryIdentifier.hpp>
#include <Acts/Geometry/TrackingGeometry.hpp>
#include <Acts/Geometry/VolumeAttachmentStrategy.hpp>
#include <Acts/MagneticField/ConstantBField.hpp>
#include <Acts/Surfaces/Surface.hpp>
#include <Acts/Utilities/AxisDefinitions.hpp>
#include <Acts/Visualization/ObjVisualization3D.hpp>
#include <ActsPlugins/DD4hep/BlueprintBuilder.hpp>
#include <ActsPlugins/DD4hep/DD4hepDetectorElement.hpp>

#include <DD4hep/DD4hepUnits.h>
#include <DD4hep/DetElement.h>
#include <DD4hep/Detector.h>

#include <GaudiKernel/StatusCode.h>

#include "Acts/Geometry/TrackingGeometry.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 <fmt/format.h>
#include <fmt/ostream.h>
#include <fmt/ranges.h>

} // namespace ActsPlugins
#endif
#include <array>

using namespace Gaudi;
template <> struct fmt::formatter<Acts::GeometryIdentifier> : 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) {
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},
{"CLD_o2_v07", FCCee::CLD_o2_v07::populateBlueprint},
{"CLD_o2_v08", FCCee::CLD_o2_v07::populateBlueprint},
{"LUXE_v0", LUXE::LUXE_v0::populateBlueprint}};
}

StatusCode ActsGeoSvc::initialize() {
m_dd4hepGeo = svcLocator()->service<IGeoSvc>(m_geoSvcName)->getDetector();
// necessary?
// m_dd4hepGeo->addExtension<IActsGeoSvc>(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);

/// Setting geometry debug option
if (m_debugGeometry == true) {
info() << "Geometry debugging is ON." << endmsg;

if (createGeoObj().isFailure()) {
error() << "Could not create geometry OBJ" << endmsg;
return StatusCode::FAILURE;
} else {
info() << "Geometry OBJ SUCCESSFULLY created" << endmsg;
}
m_geoSvc = Gaudi::svcLocator()->service<IGeoSvc>("GeoSvc");
K4_GAUDI_CHECK(m_geoSvc);

std::array<double, 3> magneticFieldVector = {0, 0, 0};
std::array<double, 3> 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::ConstantBField>(
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;

BlueprintBuilder builder{
{.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};

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);
} else {
info() << "Geometry converted without checking if GeoObj can be created" << endmsg;
error() << fmt::format("Cannot find a Blueprint construction function for detector: {}", detName) << endmsg;
return StatusCode::FAILURE;
}

return StatusCode::SUCCESS;
}
BlueprintOptions options;

StatusCode ActsGeoSvc::execute() { return StatusCode::SUCCESS; }
debug() << "Constructing tracking geometry" << endmsg;
m_trackingGeo = root.construct(options, gctxt, *gaudiLogger->cloneWithSuffix("|Construct"));

StatusCode ActsGeoSvc::finalize() { return StatusCode::SUCCESS; }

/// Create a geometry OBJ file
StatusCode ActsGeoSvc::createGeoObj() {
// Convert DD4Hep geometry to acts

Acts::ObjVisualization3D m_obj;

if (!m_trackingGeo) {
return StatusCode::FAILURE;
}
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<const ActsPlugins::DD4hepDetectorElement&>(*surface->surfacePlacement());
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 (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
Acts::ObjVisualization3D vis{4, 0.001};
m_trackingGeo->visualize(vis, gctxt);
vis.write(m_objDumpFileName.value());
}

return StatusCode::SUCCESS;
}
Loading
Loading