From e219c738149092e1bac8db00e28eba4e22654a04 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Fri, 7 Aug 2026 13:50:43 +0200 Subject: [PATCH] feat: Add SeedsToTracks Turns seeds into tracks with one track state per space-point source link, and stores the seed estimate on the innermost state. `addSeeding` built the `seed-tracks` through `SeedsToProtoTracks` and `ProtoTracksToTracks` so far. A proto track is a flat list of measurement indices, so that route loses the structure the seed carries and leaves two things resting on the order in which `seedToProtoTrack` happens to flatten the space points: which state is the innermost, and that the estimate of a seed still lines up with its proto track. `TrackParamsEstimationAlgorithm` expresses the estimate on the bottom space point's surface, which a seed names directly. The estimate is allocated once as predicted and shared as filtered and smoothed, since it is all that is known at that state. With a tracking geometry the track states also get their reference surface, which bound parameters on a state need to mean anything. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01FxPc2jP43AQuQKz8c4qsaC --- Examples/Algorithms/Utilities/CMakeLists.txt | 1 + .../ActsExamples/Utilities/SeedsToTracks.hpp | 76 +++++++++++ .../Utilities/src/SeedsToTracks.cpp | 126 ++++++++++++++++++ Python/Examples/python/reconstruction.py | 6 +- Python/Examples/src/Utilities.cpp | 5 + 5 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 Examples/Algorithms/Utilities/include/ActsExamples/Utilities/SeedsToTracks.hpp create mode 100644 Examples/Algorithms/Utilities/src/SeedsToTracks.cpp diff --git a/Examples/Algorithms/Utilities/CMakeLists.txt b/Examples/Algorithms/Utilities/CMakeLists.txt index 13bceaa34c9..1af1a619ce5 100644 --- a/Examples/Algorithms/Utilities/CMakeLists.txt +++ b/Examples/Algorithms/Utilities/CMakeLists.txt @@ -2,6 +2,7 @@ acts_add_library( ExamplesUtilities src/ProtoTracksToSeeds.cpp src/SeedsToProtoTracks.cpp + src/SeedsToTracks.cpp src/TrajectoriesToProtoTracks.cpp src/TrackSelectorAlgorithm.cpp src/TracksToTrajectories.cpp diff --git a/Examples/Algorithms/Utilities/include/ActsExamples/Utilities/SeedsToTracks.hpp b/Examples/Algorithms/Utilities/include/ActsExamples/Utilities/SeedsToTracks.hpp new file mode 100644 index 00000000000..db3528ad10f --- /dev/null +++ b/Examples/Algorithms/Utilities/include/ActsExamples/Utilities/SeedsToTracks.hpp @@ -0,0 +1,76 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/EventData/Seed.hpp" +#include "ActsExamples/EventData/Track.hpp" +#include "ActsExamples/Framework/DataHandle.hpp" +#include "ActsExamples/Framework/IAlgorithm.hpp" +#include "ActsExamples/Framework/ProcessCode.hpp" + +#include +#include + +namespace Acts { +class TrackingGeometry; +} + +namespace ActsExamples { + +/// Convert seeds into tracks with one track state per space-point source link. +/// +/// Estimated track parameters are optionally attached at track level and as +/// *predicted* parameters on the innermost state, whose surface they are +/// expressed on. State reference surfaces are optionally taken from the +/// source-link geometry identifiers. +/// +/// @note A space point can carry several source links, e.g. the two sides of a +/// strip module, and each becomes its own state. `nMeasurements` and any +/// downstream `Acts::TrackSelector::MeasurementCounter` threshold +/// therefore count source links, not space points. +class SeedsToTracks final : public IAlgorithm { + public: + struct Config { + /// Input seeds. + std::string inputSeeds = "seeds"; + /// Optional. Track parameters parallel to the input seeds. + std::string inputTrackParameters; + /// Output tracks. + std::string outputTracks = "tracks-from-seeds"; + /// Optional. Source of the track-state reference surfaces. + std::shared_ptr trackingGeometry; + }; + + /// Construct the algorithm. + /// + /// @param cfg is the algorithm configuration + /// @param logger is the logger + explicit SeedsToTracks(Config cfg, + std::unique_ptr logger = nullptr); + + /// Run the algorithm. + /// + /// @param ctx is the algorithm context with event information + /// @return a process code indication success or failure + ProcessCode execute(const AlgorithmContext& ctx) const override; + + /// Const access to the config + const Config& config() const { return m_cfg; } + + private: + Config m_cfg; + + ReadDataHandle m_inputSeeds{this, "InputSeeds"}; + ReadDataHandle m_inputTrackParameters{ + this, "InputTrackParameters"}; + WriteDataHandle m_outputTracks{this, "OutputTracks"}; +}; + +} // namespace ActsExamples diff --git a/Examples/Algorithms/Utilities/src/SeedsToTracks.cpp b/Examples/Algorithms/Utilities/src/SeedsToTracks.cpp new file mode 100644 index 00000000000..9659f864115 --- /dev/null +++ b/Examples/Algorithms/Utilities/src/SeedsToTracks.cpp @@ -0,0 +1,126 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "ActsExamples/Utilities/SeedsToTracks.hpp" + +#include "Acts/EventData/SourceLink.hpp" +#include "Acts/Geometry/TrackingGeometry.hpp" +#include "ActsExamples/EventData/IndexSourceLink.hpp" + +#include +#include +#include + +namespace ActsExamples { + +SeedsToTracks::SeedsToTracks(Config cfg, + std::unique_ptr logger) + : IAlgorithm("SeedsToTracks", std::move(logger)), m_cfg(std::move(cfg)) { + m_inputSeeds.initialize(m_cfg.inputSeeds); + m_inputTrackParameters.maybeInitialize(m_cfg.inputTrackParameters); + m_outputTracks.initialize(m_cfg.outputTracks); +} + +ProcessCode SeedsToTracks::execute(const AlgorithmContext& ctx) const { + const SeedContainer& seeds = m_inputSeeds(ctx); + ACTS_DEBUG("Received " << seeds.size() << " seeds"); + + const TrackParametersContainer* trackParameters = nullptr; + if (m_inputTrackParameters.isInitialized()) { + trackParameters = &m_inputTrackParameters(ctx); + + if (trackParameters->size() != seeds.size()) { + throw std::runtime_error( + "Number of seeds and track parameters do not match"); + } + } + + const bool hasTrackParameters = trackParameters != nullptr; + + auto trackContainer = std::make_shared(); + auto mtj = std::make_shared(); + TrackContainer tracks(trackContainer, mtj); + + for (std::size_t i = 0; i < seeds.size(); ++i) { + const auto seed = seeds.at(i); + + auto track = tracks.makeTrack(); + std::uint32_t nMeasurements = 0; + + for (const auto& sp : seed.spacePoints()) { + for (const auto& sourceLink : sp.sourceLinks()) { + // `TrackParamsEstimationAlgorithm` expresses the estimate on the + // bottom space point's surface, which is the first one here + const bool attachParameters = hasTrackParameters && + nMeasurements == 0 && + m_cfg.trackingGeometry != nullptr; + + auto trackStateProxy = track.appendTrackState( + attachParameters ? Acts::TrackStatePropMask::Predicted + : Acts::TrackStatePropMask::None); + trackStateProxy.typeFlags().setIsMeasurement(); + trackStateProxy.setUncalibratedSourceLink(Acts::SourceLink(sourceLink)); + ++nMeasurements; + + if (m_cfg.trackingGeometry != nullptr) { + const Acts::GeometryIdentifier geoId = + sourceLink.get().geometryId(); + const Acts::Surface* surface = + m_cfg.trackingGeometry->findSurface(geoId); + if (surface == nullptr) { + std::ostringstream oss; + oss << "No surface found for source-link geometry id " << geoId; + throw std::runtime_error(oss.str()); + } + trackStateProxy.setReferenceSurface(surface->getSharedPtr()); + } + + if (attachParameters) { + const auto& trackParams = trackParameters->at(i); + trackStateProxy.predicted() = trackParams.parameters(); + trackStateProxy.predictedCovariance() = + trackParams.covariance().value_or(Acts::BoundMatrix::Zero()); + // the estimate is all that is known at this state, so it stands in + // for the filtered and smoothed parameters rather than being stored + // again. that is also what lets + // `Acts::findTrackStateForExtrapolation` start from this state. + trackStateProxy.shareFrom(Acts::TrackStatePropMask::Predicted, + Acts::TrackStatePropMask::Filtered); + trackStateProxy.shareFrom(Acts::TrackStatePropMask::Predicted, + Acts::TrackStatePropMask::Smoothed); + } + } + } + + track.nMeasurements() = nMeasurements; + track.nHoles() = 0; + track.nOutliers() = 0; + + if (hasTrackParameters) { + const auto& trackParams = trackParameters->at(i); + + track.setReferenceSurface(trackParams.referenceSurface().getSharedPtr()); + track.parameters() = trackParams.parameters(); + track.covariance() = + trackParams.covariance().value_or(Acts::BoundMatrix::Zero()); + } + } + + ConstTrackContainer constTracks{ + std::make_shared( + std::move(*trackContainer)), + std::make_shared(std::move(*mtj))}; + + ACTS_DEBUG("Produced " << constTracks.size() << " tracks"); + + m_outputTracks(ctx, std::move(constTracks)); + + return ProcessCode::SUCCESS; +} + +} // namespace ActsExamples diff --git a/Python/Examples/python/reconstruction.py b/Python/Examples/python/reconstruction.py index 0ee23a3a8f8..bd56b48cc3b 100644 --- a/Python/Examples/python/reconstruction.py +++ b/Python/Examples/python/reconstruction.py @@ -544,12 +544,12 @@ def addSeeding( tracks = f"{prefix}seed-tracks" s.addAlgorithm( - acts.examples.ProtoTracksToTracks( + acts.examples.SeedsToTracks( level=logLevel, - inputProtoTracks=protoTracks, + inputSeeds=f"{prefix}estimatedseeds", inputTrackParameters=f"{prefix}estimatedparameters", - inputMeasurements=f"{prefix}measurement_subset", outputTracks=tracks, + trackingGeometry=trackingGeometry, ) ) diff --git a/Python/Examples/src/Utilities.cpp b/Python/Examples/src/Utilities.cpp index f3d48c8d1a0..7c26ff87b29 100644 --- a/Python/Examples/src/Utilities.cpp +++ b/Python/Examples/src/Utilities.cpp @@ -12,6 +12,7 @@ #include "ActsExamples/Utilities/ProtoTracksToSeeds.hpp" #include "ActsExamples/Utilities/ProtoTracksToTracks.hpp" #include "ActsExamples/Utilities/SeedsToProtoTracks.hpp" +#include "ActsExamples/Utilities/SeedsToTracks.hpp" #include "ActsExamples/Utilities/TracksToParameters.hpp" #include "ActsExamples/Utilities/TracksToTrajectories.hpp" #include "ActsExamples/Utilities/TrajectoriesToProtoTracks.hpp" @@ -41,6 +42,10 @@ void addUtilities(py::module& mex) { ACTS_PYTHON_DECLARE_ALGORITHM(SeedsToProtoTracks, mex, "SeedsToProtoTracks", inputSeeds, outputProtoTracks); + ACTS_PYTHON_DECLARE_ALGORITHM(SeedsToTracks, mex, "SeedsToTracks", inputSeeds, + inputTrackParameters, outputTracks, + trackingGeometry); + ACTS_PYTHON_DECLARE_ALGORITHM(ProtoTracksToSeeds, mex, "ProtoTracksToSeeds", inputProtoTracks, inputSpacePoints, outputSeeds, outputProtoTracks);