File indexing completed on 2026-10-17 08:39:47
0001
0002
0003
0004 #include "CKFTracking.h"
0005
0006 #include <Acts/Definitions/Algebra.hpp>
0007 #include <Acts/Definitions/TrackParametrization.hpp>
0008 #include <Acts/Definitions/Units.hpp>
0009 #include <Acts/Utilities/MathHelpers.hpp>
0010 #if Acts_VERSION_MAJOR >= 46
0011 #else
0012 #include <Acts/EventData/GenericBoundTrackParameters.hpp>
0013 #endif
0014 #include <Acts/EventData/MeasurementHelpers.hpp>
0015 #include <Acts/EventData/ParticleHypothesis.hpp>
0016 #include <Acts/EventData/ProxyAccessor.hpp>
0017 #include <Acts/EventData/SourceLink.hpp>
0018 #include <Acts/EventData/TrackContainer.hpp>
0019 #include <Acts/EventData/TrackProxy.hpp>
0020 #include <Acts/EventData/TrackStatePropMask.hpp>
0021 #include <Acts/EventData/VectorMultiTrajectory.hpp>
0022 #include <Acts/EventData/VectorTrackContainer.hpp>
0023 #include <Acts/Geometry/GeometryContext.hpp>
0024 #include <Acts/Geometry/GeometryHierarchyMap.hpp>
0025 #include <Acts/Geometry/GeometryIdentifier.hpp>
0026 #include <Acts/Propagator/ActorList.hpp>
0027 #include <Acts/Propagator/EigenStepper.hpp>
0028 #include <Acts/Propagator/MaterialInteractor.hpp>
0029 #include <Acts/Propagator/Navigator.hpp>
0030 #include <Acts/Propagator/Propagator.hpp>
0031 #include <Acts/Propagator/PropagatorOptions.hpp>
0032 #include <Acts/Propagator/StandardAborters.hpp>
0033 #include <Acts/Surfaces/PerigeeSurface.hpp>
0034 #include <Acts/Surfaces/Surface.hpp>
0035 #include <Acts/TrackFinding/CombinatorialKalmanFilterExtensions.hpp>
0036 #include <Acts/TrackFinding/TrackStateCreator.hpp>
0037 #include <Acts/TrackFitting/GainMatrixUpdater.hpp>
0038 #include <Acts/Utilities/CalibrationContext.hpp>
0039 #include <Acts/Utilities/Logger.hpp>
0040 #include <Acts/Utilities/TrackHelpers.hpp>
0041 #include <ActsExamples/EventData/GeometryContainers.hpp>
0042 #include <ActsExamples/EventData/IndexSourceLink.hpp>
0043 #include <ActsExamples/EventData/Track.hpp>
0044 #include <boost/container/vector.hpp>
0045 #include <edm4eic/Cov3f.h>
0046 #include <edm4eic/Cov6f.h>
0047 #include <edm4eic/Measurement2DCollection.h>
0048 #include <edm4eic/TrackParametersCollection.h>
0049 #include <edm4eic/TrackSeedCollection.h>
0050 #include <edm4eic/unit_system.h>
0051 #include <edm4hep/Vector2f.h>
0052 #include <spdlog/common.h>
0053 #include <Eigen/Core>
0054 #include <Eigen/Geometry>
0055 #include <Eigen/LU> // IWYU pragma: keep
0056 #include <algorithm>
0057 #include <any>
0058 #include <array>
0059 #include <cmath>
0060 #include <cstddef>
0061 #include <cstdint>
0062 #include <format>
0063 #include <functional>
0064 #include <string>
0065 #include <system_error>
0066 #include <tuple>
0067 #include <utility>
0068
0069
0070
0071 #include "ActsGeometryProvider.h"
0072 #include "extensions/edm4eic/EDM4eicToActs.h"
0073 #include "extensions/spdlog/SpdlogFormatters.h" // IWYU pragma: keep
0074 #include "extensions/spdlog/SpdlogToActs.h"
0075
0076 namespace {
0077
0078
0079
0080 class EDM4eicMeasurementSourceLinkCalibrator {
0081 public:
0082 EDM4eicMeasurementSourceLinkCalibrator(const edm4eic::Measurement2DCollection* meas2Ds,
0083 bool useTime)
0084 : m_meas2Ds(meas2Ds), m_useTime(useTime) {
0085 for (std::size_t index = 0; index < meas2Ds->size(); ++index) {
0086 m_orderedSourceLinks.emplace(Acts::GeometryIdentifier{(*meas2Ds)[index].getSurface()}, index);
0087 }
0088 }
0089
0090 const ActsExamples::GeometryIdMultiset<ActsExamples::IndexSourceLink>&
0091 orderedSourceLinks() const {
0092 return m_orderedSourceLinks;
0093 }
0094
0095 void calibrate(const Acts::GeometryContext& , const Acts::CalibrationContext& ,
0096 const Acts::SourceLink& sourceLink,
0097 Acts::VectorMultiTrajectory::TrackStateProxy trackState) const {
0098 trackState.setUncalibratedSourceLink(Acts::SourceLink{sourceLink});
0099 const auto& idxSourceLink = sourceLink.get<ActsExamples::IndexSourceLink>();
0100 const auto& meas2D = (*m_meas2Ds)[idxSourceLink.index()];
0101
0102 if (m_useTime) {
0103 calibrateImpl<3>(meas2D, trackState);
0104 } else {
0105 calibrateImpl<2>(meas2D, trackState);
0106 }
0107 }
0108
0109 private:
0110
0111 template <std::size_t N>
0112 static void calibrateImpl(const edm4eic::Measurement2D& meas2D,
0113 Acts::VectorMultiTrajectory::TrackStateProxy& trackState) {
0114 #if Acts_VERSION_MAJOR > 45 || (Acts_VERSION_MAJOR == 45 && Acts_VERSION_MINOR >= 2)
0115 Acts::Vector<N> loc = Acts::Vector<N>::Zero();
0116 Acts::SquareMatrix<N> cov = Acts::SquareMatrix<N>::Zero();
0117 #else
0118 Acts::ActsVector<N> loc = Acts::ActsVector<N>::Zero();
0119 Acts::ActsSquareMatrix<N> cov = Acts::ActsSquareMatrix<N>::Zero();
0120 #endif
0121
0122 constexpr auto indices = []() {
0123 std::array<uint8_t, N> idx{static_cast<uint8_t>(Acts::eBoundLoc0),
0124 static_cast<uint8_t>(Acts::eBoundLoc1)};
0125 if constexpr (N == 3) {
0126 idx[2] = static_cast<uint8_t>(Acts::eBoundTime);
0127 }
0128 return idx;
0129 }();
0130
0131 constexpr auto position = [indices](Acts::BoundIndices bound) {
0132 return static_cast<std::size_t>(std::find(indices.begin(), indices.end(), bound) -
0133 indices.begin());
0134 };
0135 constexpr std::size_t iLoc0 = position(Acts::eBoundLoc0);
0136 constexpr std::size_t iLoc1 = position(Acts::eBoundLoc1);
0137
0138 constexpr auto mm = Acts::UnitConstants::mm / edm4eic::unit::mm;
0139 constexpr auto mm2 = mm * mm;
0140 loc[iLoc0] = meas2D.getLoc().a * mm;
0141 loc[iLoc1] = meas2D.getLoc().b * mm;
0142 cov(iLoc0, iLoc0) = meas2D.getCovariance().xx * mm2;
0143 cov(iLoc1, iLoc1) = meas2D.getCovariance().yy * mm2;
0144 cov(iLoc0, iLoc1) = meas2D.getCovariance().xy * mm2;
0145 cov(iLoc1, iLoc0) = meas2D.getCovariance().xy * mm2;
0146
0147 if constexpr (N == 3) {
0148 constexpr std::size_t iTime = position(Acts::eBoundTime);
0149 constexpr auto ns = Acts::UnitConstants::ns / edm4eic::unit::ns;
0150 constexpr auto ns2 = ns * ns;
0151
0152 loc[iTime] = meas2D.getTime() * ns;
0153 cov(iTime, iTime) = meas2D.getCovariance().zz * ns2;
0154 cov(iLoc0, iTime) = meas2D.getCovariance().xz * mm * ns;
0155 cov(iTime, iLoc0) = cov(iLoc0, iTime);
0156 cov(iLoc1, iTime) = meas2D.getCovariance().yz * mm * ns;
0157 cov(iTime, iLoc1) = cov(iLoc1, iTime);
0158 }
0159
0160 trackState.allocateCalibrated(loc, cov);
0161 trackState.setProjectorSubspaceIndices(indices);
0162 }
0163
0164 const edm4eic::Measurement2DCollection* m_meas2Ds;
0165 bool m_useTime;
0166 ActsExamples::GeometryIdMultiset<ActsExamples::IndexSourceLink> m_orderedSourceLinks;
0167 };
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185 class CKFBranchStopper {
0186 public:
0187 using TrackProxy = ActsExamples::TrackContainer::TrackProxy;
0188 using TrackStateProxy = ActsExamples::TrackContainer::TrackStateProxy;
0189 using Result = Acts::CombinatorialKalmanFilterBranchStopperResult;
0190 using LogFunc = std::function<void(const std::string&)>;
0191
0192 CKFBranchStopper(const eicrecon::CKFTrackingConfig& cfg, LogFunc log)
0193 : m_cfg(cfg), m_log(std::move(log)) {}
0194
0195 Result operator()(const TrackProxy& track, const TrackStateProxy& trackState) const {
0196 #if Acts_VERSION_MAJOR >= 45
0197 if (!trackState.typeFlags().hasMeasurement() || !trackState.hasFiltered()) {
0198 #else
0199 if (!trackState.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag) ||
0200 !trackState.hasFiltered()) {
0201 #endif
0202 return Result::Continue;
0203 }
0204
0205 const double varQOverP =
0206 trackState.filteredCovariance()(Acts::eBoundQOverP, Acts::eBoundQOverP);
0207
0208
0209 if (!(varQOverP > 0.) || varQOverP > m_cfg.maxQOverPVariance) {
0210 if (m_log) {
0211 m_log(std::format("Dropping diverged CKF branch with {} measurement(s): "
0212 "filtered var(q/p) = {:g}, limit {:g}",
0213 track.nMeasurements(), varQOverP, m_cfg.maxQOverPVariance));
0214 }
0215 return Result::StopAndDrop;
0216 }
0217 return Result::Continue;
0218 }
0219
0220 private:
0221 const eicrecon::CKFTrackingConfig& m_cfg;
0222 LogFunc m_log;
0223 };
0224
0225 }
0226
0227 namespace eicrecon {
0228
0229 using namespace Acts::UnitLiterals;
0230
0231 void CKFTracking::init() {
0232 m_acts_logger = Acts::getDefaultLogger(
0233 "CKF", eicrecon::SpdlogToActsLevel(static_cast<spdlog::level::level_enum>(this->level())));
0234
0235
0236 m_sourcelinkSelectorCfg = {
0237 {Acts::GeometryIdentifier(),
0238 {.etaBins = m_cfg.etaBins,
0239 .chi2CutOff = m_cfg.chi2CutOff,
0240 .numMeasurementsCutOff = {m_cfg.numMeasurementsCutOff.begin(),
0241 m_cfg.numMeasurementsCutOff.end()}}},
0242 };
0243 m_trackFinderFunc = CKFTracking::makeCKFTrackingFunction(
0244 m_geoSvc->trackingGeometry(), m_geoSvc->getFieldProvider(), acts_logger());
0245 }
0246
0247 void CKFTracking::process(const Input& input, const Output& output) const {
0248 const auto [init_trk_seeds, meas2Ds] = input;
0249 auto [output_track_states, output_tracks] = output;
0250
0251
0252 if (meas2Ds->empty() || init_trk_seeds->empty()) {
0253 debug("No seeds or measurements, creating empty output containers");
0254 *output_track_states = new Acts::ConstVectorMultiTrajectory();
0255 *output_tracks = new Acts::ConstVectorTrackContainer();
0256 return;
0257 }
0258
0259
0260 if (m_cfg.useTime) {
0261 bool invalid = false;
0262 for (const auto& meas2D : *meas2Ds) {
0263 const float var_t = meas2D.getCovariance().zz;
0264 if (!std::isfinite(var_t) || var_t <= 0) {
0265 error("UseTime is enabled but measurement on surface {} has invalid time variance {}; "
0266 "skipping tracking for this event",
0267 meas2D.getSurface(), var_t);
0268 invalid = true;
0269 break;
0270 }
0271 }
0272 if (invalid) {
0273 *output_track_states = new Acts::ConstVectorMultiTrajectory();
0274 *output_tracks = new Acts::ConstVectorTrackContainer();
0275 return;
0276 }
0277 }
0278
0279 ActsExamples::TrackParametersContainer acts_init_trk_params;
0280 for (const auto& track_seed : *init_trk_seeds) {
0281
0282 const auto& track_parameter = track_seed.getParams();
0283
0284 Acts::BoundVector params;
0285 params(Acts::eBoundLoc0) =
0286 track_parameter.getLoc().a * Acts::UnitConstants::mm;
0287 params(Acts::eBoundLoc1) =
0288 track_parameter.getLoc().b * Acts::UnitConstants::mm;
0289 params(Acts::eBoundPhi) = track_parameter.getPhi();
0290 params(Acts::eBoundTheta) = track_parameter.getTheta();
0291 params(Acts::eBoundQOverP) = track_parameter.getQOverP() / Acts::UnitConstants::GeV;
0292 params(Acts::eBoundTime) = track_parameter.getTime() * Acts::UnitConstants::ns;
0293
0294 #if Acts_VERSION_MAJOR > 45 || (Acts_VERSION_MAJOR == 45 && Acts_VERSION_MINOR >= 1)
0295 Acts::BoundMatrix cov = Acts::BoundMatrix::Zero();
0296 #else
0297 Acts::BoundSquareMatrix cov = Acts::BoundSquareMatrix::Zero();
0298 #endif
0299 for (std::size_t i = 0; const auto& [a, x] : edm4eic_indexed_units) {
0300 for (std::size_t j = 0; const auto& [b, y] : edm4eic_indexed_units) {
0301 cov(a, b) = track_parameter.getCovariance()(i, j) * x * y;
0302 ++j;
0303 }
0304 ++i;
0305 }
0306
0307
0308 auto pSurface = Acts::Surface::makeShared<const Acts::PerigeeSurface>(Acts::Vector3(0, 0, 0));
0309
0310
0311 acts_init_trk_params.emplace_back(pSurface, params, cov, Acts::ParticleHypothesis::pion());
0312 }
0313
0314
0315 auto pSurface = Acts::Surface::makeShared<Acts::PerigeeSurface>(Acts::Vector3{0., 0., 0.});
0316
0317
0318 const auto spdlog_level = static_cast<spdlog::level::level_enum>(this->level());
0319 const auto acts_level = eicrecon::SpdlogToActsLevel(spdlog_level);
0320 ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("CKF", acts_level));
0321
0322
0323 const auto& gctx = m_geoSvc->getActsGeometryContext();
0324 const auto& mctx = m_geoSvc->getActsMagneticFieldContext();
0325 const auto& cctx = m_geoSvc->getActsCalibrationContext();
0326
0327 Acts::PropagatorPlainOptions pOptions(gctx, mctx);
0328 pOptions.maxSteps = 10000;
0329
0330 EDM4eicMeasurementSourceLinkCalibrator calibratorImpl{meas2Ds, m_cfg.useTime};
0331 Acts::GainMatrixUpdater kfUpdater;
0332 Acts::MeasurementSelector measSel{m_sourcelinkSelectorCfg};
0333
0334 Acts::CombinatorialKalmanFilterExtensions<ActsExamples::TrackContainer> extensions;
0335 extensions.updater.connect<&Acts::GainMatrixUpdater::operator()<
0336 typename ActsExamples::TrackContainer::TrackStateContainerBackend>>(&kfUpdater);
0337
0338 ActsExamples::IndexSourceLinkAccessor slAccessor;
0339 slAccessor.container = &calibratorImpl.orderedSourceLinks();
0340 using TrackStateCreatorType =
0341 Acts::TrackStateCreator<ActsExamples::IndexSourceLinkAccessor::Iterator,
0342 ActsExamples::TrackContainer>;
0343 TrackStateCreatorType trackStateCreator;
0344 trackStateCreator.sourceLinkAccessor
0345 .template connect<&ActsExamples::IndexSourceLinkAccessor::range>(&slAccessor);
0346 trackStateCreator.calibrator.template connect<&EDM4eicMeasurementSourceLinkCalibrator::calibrate>(
0347 &calibratorImpl);
0348 trackStateCreator.measurementSelector
0349 .template connect<&Acts::MeasurementSelector::select<Acts::VectorMultiTrajectory>>(&measSel);
0350
0351 extensions.createTrackStates.template connect<&TrackStateCreatorType::createTrackStates>(
0352 &trackStateCreator);
0353
0354
0355 CKFBranchStopper branchStopper{m_cfg, [this](const std::string& msg) { debug("{}", msg); }};
0356 extensions.branchStopper.connect<&CKFBranchStopper::operator()>(&branchStopper);
0357
0358
0359 CKFTracking::TrackFinderOptions options(gctx, mctx, cctx, extensions, pOptions);
0360
0361 using Extrapolator = Acts::Propagator<Acts::EigenStepper<>, Acts::Navigator>;
0362 using ExtrapolatorOptions = Extrapolator::template Options<
0363 Acts::ActorList<Acts::MaterialInteractor, Acts::EndOfWorldReached>>;
0364 Extrapolator extrapolator(Acts::EigenStepper<>(m_BField),
0365 Acts::Navigator({.trackingGeometry = m_geoSvc->trackingGeometry()},
0366 acts_logger().cloneWithSuffix("Navigator")),
0367 acts_logger().cloneWithSuffix("Propagator"));
0368 ExtrapolatorOptions extrapolationOptions(gctx, mctx);
0369
0370
0371 auto trackContainer = std::make_shared<Acts::VectorTrackContainer>();
0372 auto trackStateContainer = std::make_shared<Acts::VectorMultiTrajectory>();
0373 ActsExamples::TrackContainer acts_tracks(trackContainer, trackStateContainer);
0374
0375
0376 auto trackContainerTemp = std::make_shared<Acts::VectorTrackContainer>();
0377 auto trackStateContainerTemp = std::make_shared<Acts::VectorMultiTrajectory>();
0378 ActsExamples::TrackContainer acts_tracks_temp(trackContainerTemp, trackStateContainerTemp);
0379
0380
0381 acts_tracks.addColumn<unsigned int>("seed");
0382 acts_tracks_temp.addColumn<unsigned int>("seed");
0383 Acts::ProxyAccessor<unsigned int> seedNumber("seed");
0384
0385
0386 for (std::size_t iseed = 0; iseed < acts_init_trk_params.size(); ++iseed) {
0387
0388
0389 acts_tracks_temp.clear();
0390
0391
0392 auto result = (*m_trackFinderFunc)(acts_init_trk_params.at(iseed), options, acts_tracks_temp);
0393
0394 if (!result.ok()) {
0395 debug("Track finding failed for seed {} with error {}", iseed, result.error().message());
0396 continue;
0397 }
0398
0399
0400 auto& tracksForSeed = result.value();
0401 for (auto& track : tracksForSeed) {
0402
0403
0404 auto lastMeasurement = Acts::findLastMeasurementState(track);
0405 if (!lastMeasurement.ok()) {
0406 debug("Track {} for seed {} has no valid measurements, skipping", track.index(), iseed);
0407 continue;
0408 }
0409
0410 if (track.nMeasurements() < m_cfg.numMeasurementsMin) {
0411 trace("Track {} for seed {} has fewer measurements than minimum of {}, skipping",
0412 track.index(), iseed, m_cfg.numMeasurementsMin);
0413 continue;
0414 }
0415
0416 auto smoothingResult = Acts::smoothTrack(gctx, track, acts_logger());
0417 if (!smoothingResult.ok()) {
0418 debug("Smoothing for seed {} and track {} failed with error {}", iseed, track.index(),
0419 smoothingResult.error().message());
0420 continue;
0421 }
0422
0423 auto extrapolationResult = Acts::extrapolateTrackToReferenceSurface(
0424 track, *pSurface, extrapolator, extrapolationOptions,
0425 Acts::TrackExtrapolationStrategy::firstOrLast, acts_logger());
0426
0427 if (!extrapolationResult.ok()) {
0428 debug("Extrapolation for seed {} and track {} failed with error {}", iseed, track.index(),
0429 extrapolationResult.error().message());
0430 continue;
0431 }
0432
0433 seedNumber(track) = iseed;
0434
0435
0436 auto acts_tracks_proxy = acts_tracks.makeTrack();
0437 acts_tracks_proxy.copyFrom(track);
0438 }
0439 }
0440
0441
0442 *output_track_states = new Acts::ConstVectorMultiTrajectory(std::move(*trackStateContainer));
0443 *output_tracks = new Acts::ConstVectorTrackContainer(std::move(*trackContainer));
0444 }
0445
0446 }