Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-15 08:17:46

0001 // SPDX-License-Identifier: LGPL-3.0-or-later
0002 // Copyright (C) 2022 Whitney Armstrong, Wouter Deconinck, Dmitry Romanov, Shujie Li
0003 
0004 #include "CKFTracking.h"
0005 
0006 #include <Acts/Definitions/Algebra.hpp>
0007 #include <Acts/Definitions/Common.hpp>
0008 #include <Acts/Definitions/Direction.hpp>
0009 #include <Acts/Definitions/TrackParametrization.hpp>
0010 #include <Acts/Definitions/Units.hpp>
0011 #include <Acts/EventData/GenericBoundTrackParameters.hpp>
0012 #include <Acts/EventData/MeasurementHelpers.hpp>
0013 #include <Acts/EventData/TrackStatePropMask.hpp>
0014 #include <Acts/EventData/Types.hpp>
0015 #include <Acts/Geometry/GeometryHierarchyMap.hpp>
0016 #if Acts_VERSION_MAJOR >= 39
0017 #include <Acts/TrackFinding/CombinatorialKalmanFilterExtensions.hpp>
0018 #endif
0019 #if (Acts_VERSION_MAJOR >= 37) && (Acts_VERSION_MAJOR < 43)
0020 #include <Acts/Utilities/Iterator.hpp>
0021 #endif
0022 #include <Acts/Utilities/detail/ContextType.hpp>
0023 #if Acts_VERSION_MAJOR < 36
0024 #include <Acts/EventData/Measurement.hpp>
0025 #endif
0026 #include <Acts/EventData/MultiTrajectory.hpp>
0027 #include <Acts/EventData/ParticleHypothesis.hpp>
0028 #include <Acts/EventData/ProxyAccessor.hpp>
0029 #include <Acts/EventData/SourceLink.hpp>
0030 #include <Acts/EventData/TrackContainer.hpp>
0031 #include <Acts/EventData/TrackProxy.hpp>
0032 #include <Acts/EventData/VectorMultiTrajectory.hpp>
0033 #include <Acts/EventData/VectorTrackContainer.hpp>
0034 #include <Acts/Geometry/GeometryIdentifier.hpp>
0035 #if Acts_VERSION_MAJOR >= 34
0036 #if Acts_VERSION_MAJOR >= 37
0037 #include <Acts/Propagator/ActorList.hpp>
0038 #else
0039 #include <Acts/Propagator/AbortList.hpp>
0040 #include <Acts/Propagator/ActionList.hpp>
0041 #endif
0042 #include <Acts/Propagator/EigenStepper.hpp>
0043 #include <Acts/Propagator/MaterialInteractor.hpp>
0044 #include <Acts/Propagator/Navigator.hpp>
0045 #endif
0046 #include <Acts/Propagator/Propagator.hpp>
0047 #if Acts_VERSION_MAJOR >= 36
0048 #include <Acts/Propagator/PropagatorOptions.hpp>
0049 #endif
0050 #if Acts_VERSION_MAJOR >= 34
0051 #include <Acts/Propagator/StandardAborters.hpp>
0052 #endif
0053 #include <Acts/Surfaces/PerigeeSurface.hpp>
0054 #include <Acts/Surfaces/Surface.hpp>
0055 #if Acts_VERSION_MAJOR >= 39
0056 #include <Acts/TrackFinding/TrackStateCreator.hpp>
0057 #endif
0058 #if Acts_VERSION_MAJOR < 34
0059 #include <Acts/TrackFitting/GainMatrixSmoother.hpp>
0060 #endif
0061 #include <Acts/TrackFitting/GainMatrixUpdater.hpp>
0062 #include <Acts/Utilities/Logger.hpp>
0063 #if Acts_VERSION_MAJOR >= 34
0064 #include <Acts/Utilities/TrackHelpers.hpp>
0065 #endif
0066 #include <ActsExamples/EventData/IndexSourceLink.hpp>
0067 #include <ActsExamples/EventData/Measurement.hpp>
0068 #include <ActsExamples/EventData/MeasurementCalibration.hpp>
0069 #include <ActsExamples/EventData/Track.hpp>
0070 #include <boost/container/vector.hpp>
0071 #include <edm4eic/Cov3f.h>
0072 #include <edm4eic/Cov6f.h>
0073 #include <edm4eic/Measurement2DCollection.h>
0074 #include <edm4eic/TrackParametersCollection.h>
0075 #include <edm4hep/Vector2f.h>
0076 #include <fmt/core.h>
0077 #include <fmt/format.h>
0078 #include <Eigen/Core>
0079 #include <Eigen/Geometry>
0080 #include <algorithm>
0081 #include <any>
0082 #include <array>
0083 #include <cstddef>
0084 #include <functional>
0085 #include <optional>
0086 #include <ostream>
0087 #include <set>
0088 #include <stdexcept>
0089 #include <string>
0090 #include <system_error>
0091 #include <utility>
0092 
0093 #include "ActsGeometryProvider.h"
0094 #include "DD4hepBField.h"
0095 #include "extensions/edm4eic/EDM4eicToActs.h"
0096 #include "extensions/spdlog/SpdlogFormatters.h" // IWYU pragma: keep
0097 #include "extensions/spdlog/SpdlogToActs.h"
0098 
0099 namespace eicrecon {
0100 
0101 using namespace Acts::UnitLiterals;
0102 
0103 CKFTracking::CKFTracking() = default;
0104 
0105 void CKFTracking::init(std::shared_ptr<const ActsGeometryProvider> geo_svc,
0106                        std::shared_ptr<spdlog::logger> log) {
0107   m_log         = log;
0108   m_acts_logger = eicrecon::getSpdlogLogger("CKF", m_log);
0109 
0110   m_geoSvc = geo_svc;
0111 
0112   m_BField =
0113       std::dynamic_pointer_cast<const eicrecon::BField::DD4hepBField>(m_geoSvc->getFieldProvider());
0114   m_fieldctx = eicrecon::BField::BFieldVariant(m_BField);
0115 
0116   // eta bins, chi2 and #sourclinks per surface cutoffs
0117   m_sourcelinkSelectorCfg = {
0118       {Acts::GeometryIdentifier(),
0119        {m_cfg.etaBins,
0120         m_cfg.chi2CutOff,
0121         {m_cfg.numMeasurementsCutOff.begin(), m_cfg.numMeasurementsCutOff.end()}}},
0122   };
0123   m_trackFinderFunc =
0124       CKFTracking::makeCKFTrackingFunction(m_geoSvc->trackingGeometry(), m_BField, logger());
0125 }
0126 
0127 std::tuple<std::vector<ActsExamples::Trajectories*>,
0128            std::vector<ActsExamples::ConstTrackContainer*>>
0129 CKFTracking::process(const edm4eic::TrackParametersCollection& init_trk_params,
0130                      const edm4eic::Measurement2DCollection& meas2Ds) {
0131 
0132   // Create output collections
0133   std::vector<ActsExamples::Trajectories*> acts_trajectories;
0134   // Prepare the output data with MultiTrajectory, per seed
0135   acts_trajectories.reserve(init_trk_params.size());
0136   // FIXME JANA2 std::vector<T*> requires wrapping ConstTrackContainer, instead of:
0137   //ConstTrackContainer constTracks(constTrackContainer, constTrackStateContainer);
0138   std::vector<ActsExamples::ConstTrackContainer*> constTracks_v;
0139 
0140   // If measurements or initial track parameters are empty, return early
0141   if (meas2Ds.empty() || init_trk_params.empty()) {
0142     return std::make_tuple(std::move(acts_trajectories), std::move(constTracks_v));
0143   }
0144 
0145   // create sourcelink and measurement containers
0146   auto measurements = std::make_shared<ActsExamples::MeasurementContainer>();
0147 
0148   // need list here for stable addresses
0149 #if Acts_VERSION_MAJOR < 37 || (Acts_VERSION_MAJOR == 37 && Acts_VERSION_MINOR < 1)
0150   std::list<ActsExamples::IndexSourceLink> sourceLinkStorage;
0151   ActsExamples::IndexSourceLinkContainer src_links;
0152   src_links.reserve(meas2Ds.size());
0153   std::size_t hit_index = 0;
0154 #endif
0155 
0156   for (const auto& meas2D : meas2Ds) {
0157 
0158     Acts::GeometryIdentifier geoId{meas2D.getSurface()};
0159 
0160 #if Acts_VERSION_MAJOR < 37 || (Acts_VERSION_MAJOR == 37 && Acts_VERSION_MINOR < 1)
0161     // --follow example from ACTS to create source links
0162     sourceLinkStorage.emplace_back(geoId, hit_index);
0163     ActsExamples::IndexSourceLink& sourceLink = sourceLinkStorage.back();
0164     // Add to output containers:
0165     // index map and source link container are geometry-ordered.
0166     // since the input is also geometry-ordered, new items can
0167     // be added at the end.
0168     src_links.insert(src_links.end(), sourceLink);
0169 #endif
0170     // ---
0171     // Create ACTS measurements
0172 
0173     Acts::ActsVector<2> loc = Acts::Vector2::Zero();
0174     loc[Acts::eBoundLoc0]   = meas2D.getLoc().a;
0175     loc[Acts::eBoundLoc1]   = meas2D.getLoc().b;
0176 
0177     Acts::ActsSquareMatrix<2> cov = Acts::ActsSquareMatrix<2>::Zero();
0178     cov(0, 0)                     = meas2D.getCovariance().xx;
0179     cov(1, 1)                     = meas2D.getCovariance().yy;
0180     cov(0, 1)                     = meas2D.getCovariance().xy;
0181     cov(1, 0)                     = meas2D.getCovariance().xy;
0182 
0183 #if Acts_VERSION_MAJOR > 37 || (Acts_VERSION_MAJOR == 37 && Acts_VERSION_MINOR >= 1)
0184     std::array<Acts::BoundIndices, 2> indices{Acts::eBoundLoc0, Acts::eBoundLoc1};
0185     Acts::visit_measurement(
0186         indices.size(), [&](auto dim) -> ActsExamples::VariableBoundMeasurementProxy {
0187           if constexpr (dim == indices.size()) {
0188             return ActsExamples::VariableBoundMeasurementProxy{
0189                 measurements->emplaceMeasurement<dim>(geoId, indices, loc, cov)};
0190           } else {
0191             throw std::runtime_error("Dimension not supported in measurement creation");
0192           }
0193         });
0194 #elif Acts_VERSION_MAJOR == 37 && Acts_VERSION_MINOR == 0
0195     std::array<Acts::BoundIndices, 2> indices{Acts::eBoundLoc0, Acts::eBoundLoc1};
0196     Acts::visit_measurement(
0197         indices.size(), [&](auto dim) -> ActsExamples::VariableBoundMeasurementProxy {
0198           if constexpr (dim == indices.size()) {
0199             return ActsExamples::VariableBoundMeasurementProxy{
0200                 measurements->emplaceMeasurement<dim>(Acts::SourceLink{sourceLink}, indices, loc,
0201                                                       cov)};
0202           } else {
0203             throw std::runtime_error("Dimension not supported in measurement creation");
0204           }
0205         });
0206 #elif Acts_VERSION_MAJOR == 36 && Acts_VERSION_MINOR >= 1
0207     auto measurement = ActsExamples::makeVariableSizeMeasurement(
0208         Acts::SourceLink{sourceLink}, loc, cov, Acts::eBoundLoc0, Acts::eBoundLoc1);
0209     measurements->emplace_back(std::move(measurement));
0210 #elif Acts_VERSION_MAJOR == 36 && Acts_VERSION_MINOR == 0
0211     auto measurement = ActsExamples::makeFixedSizeMeasurement(
0212         Acts::SourceLink{sourceLink}, loc, cov, Acts::eBoundLoc0, Acts::eBoundLoc1);
0213     measurements->emplace_back(std::move(measurement));
0214 #else
0215     auto measurement = Acts::makeMeasurement(Acts::SourceLink{sourceLink}, loc, cov,
0216                                              Acts::eBoundLoc0, Acts::eBoundLoc1);
0217     measurements->emplace_back(std::move(measurement));
0218 #endif
0219 
0220 #if Acts_VERSION_MAJOR < 37 || (Acts_VERSION_MAJOR == 37 && Acts_VERSION_MINOR < 1)
0221     hit_index++;
0222 #endif
0223   }
0224 
0225   ActsExamples::TrackParametersContainer acts_init_trk_params;
0226   for (const auto& track_parameter : init_trk_params) {
0227 
0228     Acts::BoundVector params;
0229     params(Acts::eBoundLoc0) =
0230         track_parameter.getLoc().a * Acts::UnitConstants::mm; // cylinder radius
0231     params(Acts::eBoundLoc1) =
0232         track_parameter.getLoc().b * Acts::UnitConstants::mm; // cylinder length
0233     params(Acts::eBoundPhi)    = track_parameter.getPhi();
0234     params(Acts::eBoundTheta)  = track_parameter.getTheta();
0235     params(Acts::eBoundQOverP) = track_parameter.getQOverP() / Acts::UnitConstants::GeV;
0236     params(Acts::eBoundTime)   = track_parameter.getTime() * Acts::UnitConstants::ns;
0237 
0238     Acts::BoundSquareMatrix cov = Acts::BoundSquareMatrix::Zero();
0239     for (std::size_t i = 0; const auto& [a, x] : edm4eic_indexed_units) {
0240       for (std::size_t j = 0; const auto& [b, y] : edm4eic_indexed_units) {
0241         cov(a, b) = track_parameter.getCovariance()(i, j) * x * y;
0242         ++j;
0243       }
0244       ++i;
0245     }
0246 
0247     // Construct a perigee surface as the target surface
0248     auto pSurface = Acts::Surface::makeShared<const Acts::PerigeeSurface>(Acts::Vector3(0, 0, 0));
0249 
0250     // Create parameters
0251     acts_init_trk_params.emplace_back(pSurface, params, cov, Acts::ParticleHypothesis::pion());
0252   }
0253 
0254   //// Construct a perigee surface as the target surface
0255   auto pSurface = Acts::Surface::makeShared<Acts::PerigeeSurface>(Acts::Vector3{0., 0., 0.});
0256 
0257   ACTS_LOCAL_LOGGER(eicrecon::getSpdlogLogger("CKF", m_log, {"^No tracks found$"}));
0258 
0259 #if Acts_VERSION_MAJOR >= 36
0260   Acts::PropagatorPlainOptions pOptions(m_geoctx, m_fieldctx);
0261 #else
0262   Acts::PropagatorPlainOptions pOptions;
0263 #endif
0264   pOptions.maxSteps = 10000;
0265 
0266   ActsExamples::PassThroughCalibrator pcalibrator;
0267   ActsExamples::MeasurementCalibratorAdapter calibrator(pcalibrator, *measurements);
0268   Acts::GainMatrixUpdater kfUpdater;
0269 #if Acts_VERSION_MAJOR < 34
0270   Acts::GainMatrixSmoother kfSmoother;
0271 #endif
0272   Acts::MeasurementSelector measSel{m_sourcelinkSelectorCfg};
0273 
0274 #if Acts_VERSION_MAJOR >= 36
0275   Acts::CombinatorialKalmanFilterExtensions<ActsExamples::TrackContainer> extensions;
0276 #else
0277   Acts::CombinatorialKalmanFilterExtensions<Acts::VectorMultiTrajectory> extensions;
0278 #endif
0279 #if Acts_VERSION_MAJOR < 39
0280   extensions.calibrator.connect<&ActsExamples::MeasurementCalibratorAdapter::calibrate>(
0281       &calibrator);
0282 #endif
0283 #if Acts_VERSION_MAJOR >= 36
0284   extensions.updater.connect<&Acts::GainMatrixUpdater::operator()<
0285       typename ActsExamples::TrackContainer::TrackStateContainerBackend>>(&kfUpdater);
0286 #else
0287   extensions.updater.connect<&Acts::GainMatrixUpdater::operator()<Acts::VectorMultiTrajectory>>(
0288       &kfUpdater);
0289 #endif
0290 #if Acts_VERSION_MAJOR < 34
0291   extensions.smoother.connect<&Acts::GainMatrixSmoother::operator()<Acts::VectorMultiTrajectory>>(
0292       &kfSmoother);
0293 #endif
0294 #if (Acts_VERSION_MAJOR >= 36) && (Acts_VERSION_MAJOR < 39)
0295   extensions.measurementSelector.connect<&Acts::MeasurementSelector::select<
0296       typename ActsExamples::TrackContainer::TrackStateContainerBackend>>(&measSel);
0297 #elif Acts_VERSION_MAJOR < 39
0298   extensions.measurementSelector
0299       .connect<&Acts::MeasurementSelector::select<Acts::VectorMultiTrajectory>>(&measSel);
0300 #endif
0301 
0302   ActsExamples::IndexSourceLinkAccessor slAccessor;
0303 #if Acts_VERSION_MAJOR > 37 || (Acts_VERSION_MAJOR == 37 && Acts_VERSION_MINOR >= 1)
0304   slAccessor.container = &measurements->orderedIndices();
0305 #else
0306   slAccessor.container = &src_links;
0307 #endif
0308 #if Acts_VERSION_MAJOR >= 39
0309   using TrackStateCreatorType =
0310       Acts::TrackStateCreator<ActsExamples::IndexSourceLinkAccessor::Iterator,
0311                               ActsExamples::TrackContainer>;
0312   TrackStateCreatorType trackStateCreator;
0313   trackStateCreator.sourceLinkAccessor
0314       .template connect<&ActsExamples::IndexSourceLinkAccessor::range>(&slAccessor);
0315   trackStateCreator.calibrator
0316       .template connect<&ActsExamples::MeasurementCalibratorAdapter::calibrate>(&calibrator);
0317   trackStateCreator.measurementSelector
0318       .template connect<&Acts::MeasurementSelector::select<Acts::VectorMultiTrajectory>>(&measSel);
0319 
0320   extensions.createTrackStates.template connect<&TrackStateCreatorType::createTrackStates>(
0321       &trackStateCreator);
0322 #else
0323   Acts::SourceLinkAccessorDelegate<ActsExamples::IndexSourceLinkAccessor::Iterator>
0324       slAccessorDelegate;
0325   slAccessorDelegate.connect<&ActsExamples::IndexSourceLinkAccessor::range>(&slAccessor);
0326 #endif
0327 
0328   // Set the CombinatorialKalmanFilter options
0329 #if Acts_VERSION_MAJOR >= 39
0330   CKFTracking::TrackFinderOptions options(m_geoctx, m_fieldctx, m_calibctx, extensions, pOptions);
0331 #elif Acts_VERSION_MAJOR >= 34
0332   CKFTracking::TrackFinderOptions options(m_geoctx, m_fieldctx, m_calibctx, slAccessorDelegate,
0333                                           extensions, pOptions);
0334 #else
0335   CKFTracking::TrackFinderOptions options(m_geoctx, m_fieldctx, m_calibctx, slAccessorDelegate,
0336                                           extensions, pOptions, &(*pSurface));
0337 #endif
0338 
0339 #if Acts_VERSION_MAJOR >= 36
0340   using Extrapolator = Acts::Propagator<Acts::EigenStepper<>, Acts::Navigator>;
0341 #if Acts_VERSION_MAJOR >= 37
0342   using ExtrapolatorOptions = Extrapolator::template Options<
0343       Acts::ActorList<Acts::MaterialInteractor, Acts::EndOfWorldReached>>;
0344 #else
0345   using ExtrapolatorOptions =
0346       Extrapolator::template Options<Acts::ActionList<Acts::MaterialInteractor>,
0347                                      Acts::AbortList<Acts::EndOfWorldReached>>;
0348 #endif
0349   Extrapolator extrapolator(
0350       Acts::EigenStepper<>(m_BField),
0351       Acts::Navigator({m_geoSvc->trackingGeometry()}, logger().cloneWithSuffix("Navigator")),
0352       logger().cloneWithSuffix("Propagator"));
0353   ExtrapolatorOptions extrapolationOptions(m_geoctx, m_fieldctx);
0354 #elif Acts_VERSION_MAJOR >= 34
0355   Acts::Propagator<Acts::EigenStepper<>, Acts::Navigator> extrapolator(
0356       Acts::EigenStepper<>(m_BField),
0357       Acts::Navigator({m_geoSvc->trackingGeometry()}, logger().cloneWithSuffix("Navigator")),
0358       logger().cloneWithSuffix("Propagator"));
0359   Acts::PropagatorOptions<Acts::ActionList<Acts::MaterialInteractor>,
0360                           Acts::AbortList<Acts::EndOfWorldReached>>
0361       extrapolationOptions(m_geoctx, m_fieldctx);
0362 #endif
0363 
0364   // Create track container
0365   auto trackContainer      = std::make_shared<Acts::VectorTrackContainer>();
0366   auto trackStateContainer = std::make_shared<Acts::VectorMultiTrajectory>();
0367   ActsExamples::TrackContainer acts_tracks(trackContainer, trackStateContainer);
0368 
0369   // Add seed number column
0370   acts_tracks.addColumn<unsigned int>("seed");
0371   Acts::ProxyAccessor<unsigned int> seedNumber("seed");
0372   std::set<Acts::TrackIndexType> passed_tracks;
0373 
0374   // Loop over seeds
0375   for (std::size_t iseed = 0; iseed < acts_init_trk_params.size(); ++iseed) {
0376     auto result = (*m_trackFinderFunc)(acts_init_trk_params.at(iseed), options, acts_tracks);
0377 
0378     if (!result.ok()) {
0379       m_log->debug("Track finding failed for seed {} with error {}", iseed, result.error());
0380       continue;
0381     }
0382 
0383     // Set seed number for all found tracks
0384     auto& tracksForSeed = result.value();
0385     for (auto& track : tracksForSeed) {
0386 #if Acts_VERSION_MAJOR >= 34
0387       auto smoothingResult = Acts::smoothTrack(m_geoctx, track, logger());
0388       if (!smoothingResult.ok()) {
0389         ACTS_ERROR("Smoothing for seed " << iseed << " and track " << track.index()
0390                                          << " failed with error " << smoothingResult.error());
0391         continue;
0392       }
0393 
0394       auto extrapolationResult = Acts::extrapolateTrackToReferenceSurface(
0395           track, *pSurface, extrapolator, extrapolationOptions,
0396           Acts::TrackExtrapolationStrategy::firstOrLast, logger());
0397 
0398       if (!extrapolationResult.ok()) {
0399         ACTS_ERROR("Extrapolation for seed " << iseed << " and track " << track.index()
0400                                              << " failed with error "
0401                                              << extrapolationResult.error());
0402         continue;
0403       }
0404 #endif
0405 
0406       passed_tracks.insert(track.index());
0407       seedNumber(track) = iseed;
0408     }
0409   }
0410 
0411   for (std::size_t track_index = acts_tracks.size(); (track_index--) != 0U;) {
0412     if (!passed_tracks.contains(track_index)) {
0413       // NOTE This does not remove track states corresponding to the
0414       // removed tracks. Doing so would require implementing some garbage
0415       // collection. We'll just assume no algorithm will access them
0416       // directly.
0417       acts_tracks.removeTrack(track_index);
0418 #if Acts_VERSION_MAJOR < 36 || (Acts_VERSION_MAJOR == 36 && Acts_VERSION_MINOR < 1)
0419       // Workaround an upstream bug in Acts::VectorTrackContainer::removeTrack_impl()
0420       // https://github.com/acts-project/acts/commit/94cf81f3f1109210b963977e0904516b949b1154
0421       trackContainer->m_particleHypothesis.erase(trackContainer->m_particleHypothesis.begin() +
0422                                                  track_index);
0423 #endif
0424     }
0425   }
0426 
0427   // Move track states and track container to const containers
0428   // NOTE Using the non-const containers leads to references to
0429   // implicitly converted temporaries inside the Trajectories.
0430   auto constTrackStateContainer =
0431       std::make_shared<Acts::ConstVectorMultiTrajectory>(std::move(*trackStateContainer));
0432 
0433   auto constTrackContainer =
0434       std::make_shared<Acts::ConstVectorTrackContainer>(std::move(*trackContainer));
0435 
0436   constTracks_v.push_back(
0437       new ActsExamples::ConstTrackContainer(constTrackContainer, constTrackStateContainer));
0438   auto& constTracks = *(constTracks_v.front());
0439 
0440   // Seed number column accessor
0441   const Acts::ConstProxyAccessor<unsigned int> constSeedNumber("seed");
0442 
0443   ActsExamples::Trajectories::IndexedParameters parameters;
0444   std::vector<Acts::MultiTrajectoryTraits::IndexType> tips;
0445 
0446   std::optional<unsigned int> lastSeed;
0447   for (const auto& track : constTracks) {
0448     if (!lastSeed) {
0449       lastSeed = constSeedNumber(track);
0450     }
0451 
0452     if (constSeedNumber(track) != lastSeed.value()) {
0453       // make copies and clear vectors
0454       acts_trajectories.push_back(
0455           new ActsExamples::Trajectories(constTracks.trackStateContainer(), tips, parameters));
0456 
0457       tips.clear();
0458       parameters.clear();
0459     }
0460 
0461     lastSeed = constSeedNumber(track);
0462 
0463     tips.push_back(track.tipIndex());
0464     parameters.emplace(std::pair{
0465         track.tipIndex(),
0466         ActsExamples::TrackParameters{track.referenceSurface().getSharedPtr(), track.parameters(),
0467                                       track.covariance(), track.particleHypothesis()}});
0468   }
0469 
0470   if (tips.empty()) {
0471     m_log->info("Last trajectory is empty");
0472   }
0473 
0474   // last entry: move vectors
0475   acts_trajectories.push_back(new ActsExamples::Trajectories(
0476       constTracks.trackStateContainer(), std::move(tips), std::move(parameters)));
0477 
0478   return std::make_tuple(std::move(acts_trajectories), std::move(constTracks_v));
0479 }
0480 
0481 } // namespace eicrecon