File indexing completed on 2025-08-05 08:09:48
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Direction.hpp"
0013 #include "Acts/Definitions/TrackParametrization.hpp"
0014 #include "Acts/EventData/MultiTrajectory.hpp"
0015 #include "Acts/EventData/ProxyAccessor.hpp"
0016 #include "Acts/EventData/SourceLink.hpp"
0017 #include "Acts/EventData/TrackContainer.hpp"
0018 #include "Acts/EventData/TrackParameters.hpp"
0019 #include "Acts/EventData/VectorMultiTrajectory.hpp"
0020 #include "Acts/EventData/VectorTrackContainer.hpp"
0021 #include "Acts/Geometry/GeometryIdentifier.hpp"
0022 #include "Acts/Propagator/AbortList.hpp"
0023 #include "Acts/Propagator/EigenStepper.hpp"
0024 #include "Acts/Propagator/MaterialInteractor.hpp"
0025 #include "Acts/Propagator/Navigator.hpp"
0026 #include "Acts/Propagator/Propagator.hpp"
0027 #include "Acts/Propagator/StandardAborters.hpp"
0028 #include "Acts/Surfaces/PerigeeSurface.hpp"
0029 #include "Acts/Surfaces/Surface.hpp"
0030 #include "Acts/TrackFinding/CombinatorialKalmanFilter.hpp"
0031 #include "Acts/TrackFitting/GainMatrixSmoother.hpp"
0032 #include "Acts/TrackFitting/GainMatrixUpdater.hpp"
0033 #include "Acts/TrackFitting/KalmanFitter.hpp"
0034 #include "Acts/Utilities/Delegate.hpp"
0035 #include "Acts/Utilities/Enumerate.hpp"
0036 #include "Acts/Utilities/Logger.hpp"
0037 #include "Acts/Utilities/TrackHelpers.hpp"
0038 #include "ActsExamples/EventData/IndexSourceLink.hpp"
0039 #include "ActsExamples/EventData/Measurement.hpp"
0040 #include "ActsExamples/EventData/MeasurementCalibration.hpp"
0041 #include "ActsExamples/EventData/SimSeed.hpp"
0042 #include "ActsExamples/EventData/Track.hpp"
0043 #include "ActsExamples/Framework/AlgorithmContext.hpp"
0044 #include "ActsExamples/Framework/ProcessCode.hpp"
0045
0046 #include <cmath>
0047 #include <functional>
0048 #include <memory>
0049 #include <optional>
0050 #include <ostream>
0051 #include <stdexcept>
0052 #include <system_error>
0053 #include <unordered_map>
0054 #include <utility>
0055
0056 #include <boost/functional/hash.hpp>
0057
0058
0059
0060 template <class T, std::size_t N>
0061 struct std::hash<std::array<T, N>> {
0062 std::size_t operator()(const std::array<T, N>& array) const {
0063 std::hash<T> hasher;
0064 std::size_t result = 0;
0065 for (auto&& element : array) {
0066 boost::hash_combine(result, hasher(element));
0067 }
0068 return result;
0069 }
0070 };
0071
0072 namespace ActsExamples {
0073
0074 namespace {
0075
0076 class MeasurementSelector {
0077 public:
0078 using Traj = Acts::VectorMultiTrajectory;
0079
0080 explicit MeasurementSelector(Acts::MeasurementSelector selector)
0081 : m_selector(std::move(selector)) {}
0082
0083 void setSeed(const std::optional<SimSeed>& seed) { m_seed = seed; }
0084
0085 Acts::Result<std::pair<std::vector<Traj::TrackStateProxy>::iterator,
0086 std::vector<Traj::TrackStateProxy>::iterator>>
0087 select(std::vector<Traj::TrackStateProxy>& candidates, bool& isOutlier,
0088 const Acts::Logger& logger) const {
0089 if (m_seed.has_value()) {
0090 std::vector<Traj::TrackStateProxy> newCandidates;
0091
0092 for (const auto& candidate : candidates) {
0093 if (isSeedCandidate(candidate)) {
0094 newCandidates.push_back(candidate);
0095 }
0096 }
0097
0098 if (!newCandidates.empty()) {
0099 candidates = std::move(newCandidates);
0100 }
0101 }
0102
0103 return m_selector.select<Acts::VectorMultiTrajectory>(candidates, isOutlier,
0104 logger);
0105 }
0106
0107 private:
0108 Acts::MeasurementSelector m_selector;
0109 std::optional<SimSeed> m_seed;
0110
0111 bool isSeedCandidate(const Traj::TrackStateProxy& candidate) const {
0112 assert(candidate.hasUncalibratedSourceLink());
0113
0114 const Acts::SourceLink& sourceLink = candidate.getUncalibratedSourceLink();
0115
0116 for (const auto& sp : m_seed->sp()) {
0117 for (const auto& sl : sp->sourceLinks()) {
0118 if (sourceLink.get<IndexSourceLink>() == sl.get<IndexSourceLink>()) {
0119 return true;
0120 }
0121 }
0122 }
0123
0124 return false;
0125 }
0126 };
0127
0128
0129
0130 using SeedIdentifier = std::array<Index, 3>;
0131
0132
0133
0134
0135
0136 SeedIdentifier makeSeedIdentifier(const SimSeed& seed) {
0137 SeedIdentifier result;
0138
0139 for (const auto& [i, sp] : Acts::enumerate(seed.sp())) {
0140 const Acts::SourceLink& firstSourceLink = sp->sourceLinks().front();
0141 result.at(i) = firstSourceLink.get<IndexSourceLink>().index();
0142 }
0143
0144 return result;
0145 }
0146
0147
0148
0149
0150
0151 template <typename Visitor>
0152 void visitSeedIdentifiers(const TrackProxy& track, Visitor visitor) {
0153
0154 std::vector<Index> sourceLinkIndices;
0155 sourceLinkIndices.reserve(track.nMeasurements());
0156 for (const auto& trackState : track.trackStatesReversed()) {
0157 if (!trackState.hasUncalibratedSourceLink()) {
0158 continue;
0159 }
0160 const Acts::SourceLink& sourceLink = trackState.getUncalibratedSourceLink();
0161 sourceLinkIndices.push_back(sourceLink.get<IndexSourceLink>().index());
0162 }
0163
0164
0165 for (std::size_t i = 0; i < sourceLinkIndices.size(); ++i) {
0166 for (std::size_t j = i + 1; j < sourceLinkIndices.size(); ++j) {
0167 for (std::size_t k = j + 1; k < sourceLinkIndices.size(); ++k) {
0168
0169
0170 visitor({sourceLinkIndices.at(k), sourceLinkIndices.at(j),
0171 sourceLinkIndices.at(i)});
0172 }
0173 }
0174 }
0175 }
0176
0177 class BranchStopper {
0178 public:
0179 using Config =
0180 std::optional<std::variant<Acts::TrackSelector::Config,
0181 Acts::TrackSelector::EtaBinnedConfig>>;
0182
0183 mutable std::atomic<std::size_t> m_nStoppedBranches{0};
0184
0185 explicit BranchStopper(const Config& config) : m_config(config) {}
0186
0187 bool operator()(
0188 const Acts::CombinatorialKalmanFilterTipState& tipState,
0189 Acts::VectorMultiTrajectory::TrackStateProxy& trackState) const {
0190 if (!m_config.has_value()) {
0191 return false;
0192 }
0193
0194 const Acts::TrackSelector::Config* singleConfig = std::visit(
0195 [&](const auto& config) -> const Acts::TrackSelector::Config* {
0196 using T = std::decay_t<decltype(config)>;
0197 if constexpr (std::is_same_v<T, Acts::TrackSelector::Config>) {
0198 return &config;
0199 } else if constexpr (std::is_same_v<
0200 T, Acts::TrackSelector::EtaBinnedConfig>) {
0201 double theta = trackState.parameters()[Acts::eBoundTheta];
0202 double eta = -std::log(std::tan(0.5 * theta));
0203 return config.hasCuts(eta) ? &config.getCuts(eta) : nullptr;
0204 }
0205 },
0206 *m_config);
0207
0208 if (singleConfig == nullptr) {
0209 ++m_nStoppedBranches;
0210 return true;
0211 }
0212
0213
0214 if (tipState.nHoles <= singleConfig->maxHoles) {
0215 return false;
0216 }
0217
0218
0219 if (tipState.nOutliers <= singleConfig->maxOutliers) {
0220 return false;
0221 }
0222
0223
0224 if (tipState.nMeasurements < singleConfig->minMeasurements) {
0225 ++m_nStoppedBranches;
0226 return true;
0227 }
0228
0229
0230
0231 if (trackState.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag)) {
0232 ++m_nStoppedBranches;
0233 return true;
0234 }
0235
0236
0237
0238 return false;
0239 }
0240
0241 private:
0242 Config m_config;
0243 };
0244
0245 }
0246
0247 TrackFindingAlgorithm::TrackFindingAlgorithm(Config config,
0248 Acts::Logging::Level level)
0249 : IAlgorithm("TrackFindingAlgorithm", level), m_cfg(std::move(config)) {
0250 if (m_cfg.inputMeasurements.empty()) {
0251 throw std::invalid_argument("Missing measurements input collection");
0252 }
0253 if (m_cfg.inputSourceLinks.empty()) {
0254 throw std::invalid_argument("Missing source links input collection");
0255 }
0256 if (m_cfg.inputInitialTrackParameters.empty()) {
0257 throw std::invalid_argument(
0258 "Missing initial track parameters input collection");
0259 }
0260 if (m_cfg.outputTracks.empty()) {
0261 throw std::invalid_argument("Missing tracks output collection");
0262 }
0263
0264 if (m_cfg.seedDeduplication && m_cfg.inputSeeds.empty()) {
0265 throw std::invalid_argument(
0266 "Missing seeds input collection. This is "
0267 "required for seed deduplication.");
0268 }
0269 if (m_cfg.stayOnSeed && m_cfg.inputSeeds.empty()) {
0270 throw std::invalid_argument(
0271 "Missing seeds input collection. This is "
0272 "required for staying on seed.");
0273 }
0274
0275 if (m_cfg.trackSelectorCfg.has_value()) {
0276 m_trackSelector = std::visit(
0277 [](const auto& cfg) -> std::optional<Acts::TrackSelector> {
0278 return {cfg};
0279 },
0280 m_cfg.trackSelectorCfg.value());
0281 }
0282
0283 m_inputMeasurements.initialize(m_cfg.inputMeasurements);
0284 m_inputSourceLinks.initialize(m_cfg.inputSourceLinks);
0285 m_inputInitialTrackParameters.initialize(m_cfg.inputInitialTrackParameters);
0286 m_inputSeeds.maybeInitialize(m_cfg.inputSeeds);
0287 m_outputTracks.initialize(m_cfg.outputTracks);
0288 }
0289
0290 ProcessCode TrackFindingAlgorithm::execute(const AlgorithmContext& ctx) const {
0291
0292 const auto& measurements = m_inputMeasurements(ctx);
0293 const auto& sourceLinks = m_inputSourceLinks(ctx);
0294 const auto& initialParameters = m_inputInitialTrackParameters(ctx);
0295 const SimSeedContainer* seeds = nullptr;
0296
0297 if (m_inputSeeds.isInitialized()) {
0298 seeds = &m_inputSeeds(ctx);
0299
0300 if (initialParameters.size() != seeds->size()) {
0301 ACTS_ERROR("Number of initial parameters and seeds do not match. "
0302 << initialParameters.size() << " != " << seeds->size());
0303 }
0304 }
0305
0306
0307 auto pSurface = Acts::Surface::makeShared<Acts::PerigeeSurface>(
0308 Acts::Vector3{0., 0., 0.});
0309
0310 PassThroughCalibrator pcalibrator;
0311 MeasurementCalibratorAdapter calibrator(pcalibrator, measurements);
0312 Acts::GainMatrixUpdater kfUpdater;
0313 MeasurementSelector measSel{
0314 Acts::MeasurementSelector(m_cfg.measurementSelectorCfg)};
0315
0316 using Extensions =
0317 Acts::CombinatorialKalmanFilterExtensions<Acts::VectorMultiTrajectory>;
0318
0319 BranchStopper branchStopper(m_cfg.trackSelectorCfg);
0320
0321 Extensions extensions;
0322 extensions.calibrator.connect<&MeasurementCalibratorAdapter::calibrate>(
0323 &calibrator);
0324 extensions.updater.connect<
0325 &Acts::GainMatrixUpdater::operator()<Acts::VectorMultiTrajectory>>(
0326 &kfUpdater);
0327 extensions.measurementSelector.connect<&MeasurementSelector::select>(
0328 &measSel);
0329 extensions.branchStopper.connect<&BranchStopper::operator()>(&branchStopper);
0330
0331 IndexSourceLinkAccessor slAccessor;
0332 slAccessor.container = &sourceLinks;
0333 Acts::SourceLinkAccessorDelegate<IndexSourceLinkAccessor::Iterator>
0334 slAccessorDelegate;
0335 slAccessorDelegate.connect<&IndexSourceLinkAccessor::range>(&slAccessor);
0336
0337 Acts::PropagatorPlainOptions firstPropOptions;
0338 firstPropOptions.maxSteps = m_cfg.maxSteps;
0339 firstPropOptions.direction = Acts::Direction::Forward;
0340
0341 Acts::PropagatorPlainOptions secondPropOptions;
0342 secondPropOptions.maxSteps = m_cfg.maxSteps;
0343 secondPropOptions.direction = firstPropOptions.direction.invert();
0344
0345
0346 TrackFindingAlgorithm::TrackFinderOptions firstOptions(
0347 ctx.geoContext, ctx.magFieldContext, ctx.calibContext, slAccessorDelegate,
0348 extensions, firstPropOptions);
0349
0350 TrackFindingAlgorithm::TrackFinderOptions secondOptions(
0351 ctx.geoContext, ctx.magFieldContext, ctx.calibContext, slAccessorDelegate,
0352 extensions, secondPropOptions);
0353 secondOptions.targetSurface = pSurface.get();
0354
0355 Acts::Propagator<Acts::EigenStepper<>, Acts::Navigator> extrapolator(
0356 Acts::EigenStepper<>(m_cfg.magneticField),
0357 Acts::Navigator({m_cfg.trackingGeometry},
0358 logger().cloneWithSuffix("Navigator")),
0359 logger().cloneWithSuffix("Propagator"));
0360
0361 Acts::PropagatorOptions<Acts::ActionList<Acts::MaterialInteractor>,
0362 Acts::AbortList<Acts::EndOfWorldReached>>
0363 extrapolationOptions(ctx.geoContext, ctx.magFieldContext);
0364
0365
0366 ACTS_DEBUG("Invoke track finding with " << initialParameters.size()
0367 << " seeds.");
0368
0369 auto trackContainer = std::make_shared<Acts::VectorTrackContainer>();
0370 auto trackStateContainer = std::make_shared<Acts::VectorMultiTrajectory>();
0371
0372 auto trackContainerTemp = std::make_shared<Acts::VectorTrackContainer>();
0373 auto trackStateContainerTemp =
0374 std::make_shared<Acts::VectorMultiTrajectory>();
0375
0376 TrackContainer tracks(trackContainer, trackStateContainer);
0377 TrackContainer tracksTemp(trackContainerTemp, trackStateContainerTemp);
0378
0379 tracks.addColumn<unsigned int>("trackGroup");
0380 tracksTemp.addColumn<unsigned int>("trackGroup");
0381 Acts::ProxyAccessor<unsigned int> seedNumber("trackGroup");
0382
0383 unsigned int nSeed = 0;
0384
0385
0386 std::unordered_map<SeedIdentifier, bool> discoveredSeeds;
0387
0388 auto addTrack = [&](const TrackProxy& track) {
0389 ++m_nFoundTracks;
0390
0391
0392 visitSeedIdentifiers(track, [&](const SeedIdentifier& seedIdentifier) {
0393 if (auto it = discoveredSeeds.find(seedIdentifier);
0394 it != discoveredSeeds.end()) {
0395 it->second = true;
0396 }
0397 });
0398
0399 if (m_trackSelector.has_value() && !m_trackSelector->isValidTrack(track)) {
0400 return;
0401 }
0402
0403 ++m_nSelectedTracks;
0404
0405 auto destProxy = tracks.makeTrack();
0406
0407 destProxy.copyFrom(track, true);
0408 };
0409
0410 if (seeds != nullptr && m_cfg.seedDeduplication) {
0411
0412 for (const auto& seed : *seeds) {
0413 SeedIdentifier seedIdentifier = makeSeedIdentifier(seed);
0414 discoveredSeeds.emplace(seedIdentifier, false);
0415 }
0416 }
0417
0418 for (std::size_t iSeed = 0; iSeed < initialParameters.size(); ++iSeed) {
0419 m_nTotalSeeds++;
0420
0421 if (seeds != nullptr) {
0422 const SimSeed& seed = seeds->at(iSeed);
0423
0424 if (m_cfg.seedDeduplication) {
0425 SeedIdentifier seedIdentifier = makeSeedIdentifier(seed);
0426
0427 if (auto it = discoveredSeeds.find(seedIdentifier);
0428 it != discoveredSeeds.end() && it->second) {
0429 m_nDeduplicatedSeeds++;
0430 ACTS_VERBOSE("Skipping seed " << iSeed << " due to deduplication.");
0431 continue;
0432 }
0433 }
0434
0435 if (m_cfg.stayOnSeed) {
0436 measSel.setSeed(seed);
0437 }
0438 }
0439
0440
0441 tracksTemp.clear();
0442
0443 const Acts::BoundTrackParameters& firstInitialParameters =
0444 initialParameters.at(iSeed);
0445
0446 auto firstResult =
0447 (*m_cfg.findTracks)(firstInitialParameters, firstOptions, tracksTemp);
0448 nSeed++;
0449
0450 if (!firstResult.ok()) {
0451 m_nFailedSeeds++;
0452 ACTS_WARNING("Track finding failed for seed " << iSeed << " with error"
0453 << firstResult.error());
0454 continue;
0455 }
0456
0457 auto& firstTracksForSeed = firstResult.value();
0458 for (auto& firstTrack : firstTracksForSeed) {
0459
0460
0461
0462
0463 auto trackCandidate = tracksTemp.makeTrack();
0464 trackCandidate.copyFrom(firstTrack, true);
0465
0466 auto firstSmoothingResult =
0467 Acts::smoothTrack(ctx.geoContext, trackCandidate, logger());
0468 if (!firstSmoothingResult.ok()) {
0469 m_nFailedSmoothing++;
0470 ACTS_ERROR("First smoothing for seed "
0471 << iSeed << " and track " << firstTrack.index()
0472 << " failed with error " << firstSmoothingResult.error());
0473 continue;
0474 }
0475
0476
0477 std::size_t nSecond = 0;
0478
0479
0480
0481 seedNumber(trackCandidate) = nSeed - 1;
0482
0483 if (m_cfg.twoWay) {
0484 std::optional<Acts::VectorMultiTrajectory::TrackStateProxy>
0485 firstMeasurement;
0486 for (auto trackState : trackCandidate.trackStatesReversed()) {
0487 bool isMeasurement = trackState.typeFlags().test(
0488 Acts::TrackStateFlag::MeasurementFlag);
0489 bool isOutlier =
0490 trackState.typeFlags().test(Acts::TrackStateFlag::OutlierFlag);
0491
0492
0493
0494 if (isMeasurement && !isOutlier) {
0495 firstMeasurement = trackState;
0496 }
0497 }
0498
0499 if (firstMeasurement.has_value()) {
0500 Acts::BoundTrackParameters secondInitialParameters =
0501 trackCandidate.createParametersFromState(*firstMeasurement);
0502
0503 auto secondResult = (*m_cfg.findTracks)(secondInitialParameters,
0504 secondOptions, tracksTemp);
0505
0506 if (!secondResult.ok()) {
0507 ACTS_WARNING("Second track finding failed for seed "
0508 << iSeed << " with error" << secondResult.error());
0509 } else {
0510 auto firstState =
0511 *std::next(trackCandidate.trackStatesReversed().begin(),
0512 trackCandidate.nTrackStates() - 1);
0513 assert(firstState.previous() == Acts::kTrackIndexInvalid);
0514
0515 auto& secondTracksForSeed = secondResult.value();
0516 for (auto& secondTrack : secondTracksForSeed) {
0517 if (secondTrack.nTrackStates() < 2) {
0518 continue;
0519 }
0520
0521
0522
0523
0524
0525 auto secondTrackCopy = tracksTemp.makeTrack();
0526 secondTrackCopy.copyFrom(secondTrack, true);
0527
0528
0529
0530
0531 secondTrackCopy.reverseTrackStates(true);
0532
0533 firstState.previous() =
0534 (*std::next(secondTrackCopy.trackStatesReversed().begin()))
0535 .index();
0536
0537 Acts::calculateTrackQuantities(trackCandidate);
0538
0539
0540
0541
0542
0543
0544 auto secondExtrapolationResult =
0545 Acts::extrapolateTrackToReferenceSurface(
0546 trackCandidate, *pSurface, extrapolator,
0547 extrapolationOptions, m_cfg.extrapolationStrategy,
0548 logger());
0549 if (!secondExtrapolationResult.ok()) {
0550 m_nFailedExtrapolation++;
0551 ACTS_ERROR("Second extrapolation for seed "
0552 << iSeed << " and track " << secondTrack.index()
0553 << " failed with error "
0554 << secondExtrapolationResult.error());
0555 continue;
0556 }
0557
0558 addTrack(trackCandidate);
0559
0560 ++nSecond;
0561 }
0562
0563
0564
0565 firstState.previous() = Acts::kTrackIndexInvalid;
0566 Acts::calculateTrackQuantities(trackCandidate);
0567 }
0568 }
0569 }
0570
0571
0572 if (nSecond == 0) {
0573 auto firstExtrapolationResult =
0574 Acts::extrapolateTrackToReferenceSurface(
0575 trackCandidate, *pSurface, extrapolator, extrapolationOptions,
0576 m_cfg.extrapolationStrategy, logger());
0577 if (!firstExtrapolationResult.ok()) {
0578 m_nFailedExtrapolation++;
0579 ACTS_ERROR("Extrapolation for seed "
0580 << iSeed << " and track " << firstTrack.index()
0581 << " failed with error "
0582 << firstExtrapolationResult.error());
0583 continue;
0584 }
0585
0586 addTrack(trackCandidate);
0587 }
0588 }
0589 }
0590
0591
0592 if (m_cfg.computeSharedHits) {
0593 computeSharedHits(sourceLinks, tracks);
0594 }
0595
0596 ACTS_DEBUG("Finalized track finding with " << tracks.size()
0597 << " track candidates.");
0598
0599 m_nStoppedBranches += branchStopper.m_nStoppedBranches;
0600
0601 m_memoryStatistics.local().hist +=
0602 tracks.trackStateContainer().statistics().hist;
0603
0604 auto constTrackStateContainer =
0605 std::make_shared<Acts::ConstVectorMultiTrajectory>(
0606 std::move(*trackStateContainer));
0607
0608 auto constTrackContainer = std::make_shared<Acts::ConstVectorTrackContainer>(
0609 std::move(*trackContainer));
0610
0611 ConstTrackContainer constTracks{constTrackContainer,
0612 constTrackStateContainer};
0613
0614 m_outputTracks(ctx, std::move(constTracks));
0615 return ProcessCode::SUCCESS;
0616 }
0617
0618 ProcessCode TrackFindingAlgorithm::finalize() {
0619 ACTS_INFO("TrackFindingAlgorithm statistics:");
0620 ACTS_INFO("- total seeds: " << m_nTotalSeeds);
0621 ACTS_INFO("- deduplicated seeds: " << m_nDeduplicatedSeeds);
0622 ACTS_INFO("- failed seeds: " << m_nFailedSeeds);
0623 ACTS_INFO("- failed smoothing: " << m_nFailedSmoothing);
0624 ACTS_INFO("- failed extrapolation: " << m_nFailedExtrapolation);
0625 ACTS_INFO("- failure ratio seeds: " << static_cast<double>(m_nFailedSeeds) /
0626 m_nTotalSeeds);
0627 ACTS_INFO("- found tracks: " << m_nFoundTracks);
0628 ACTS_INFO("- selected tracks: " << m_nSelectedTracks);
0629 ACTS_INFO("- stopped branches: " << m_nStoppedBranches);
0630
0631 auto memoryStatistics =
0632 m_memoryStatistics.combine([](const auto& a, const auto& b) {
0633 Acts::VectorMultiTrajectory::Statistics c;
0634 c.hist = a.hist + b.hist;
0635 return c;
0636 });
0637 std::stringstream ss;
0638 memoryStatistics.toStream(ss);
0639 ACTS_DEBUG("Track State memory statistics (averaged):\n" << ss.str());
0640 return ProcessCode::SUCCESS;
0641 }
0642
0643 }