5 #include "Acts/Propagator/PropagatorOptions.hpp"
22 #include "Acts/Definitions/Units.hpp"
23 #include "Acts/Geometry/TrackingGeometry.hpp"
24 #include "Acts/Geometry/GeometryIdentifier.hpp"
25 #include "Acts/MagneticField/MagneticFieldProvider.hpp"
26 #include "Acts/Surfaces/Surface.hpp"
27 #include "Acts/TrackFinding/MeasurementSelector.hpp"
28 #include "Acts/TrackFinding/CombinatorialKalmanFilter.hpp"
29 #include "Acts/Surfaces/PerigeeSurface.hpp"
30 #include "Acts/Utilities/TrackHelpers.hpp"
31 #include "Acts/TrackFitting/MbfSmoother.hpp"
59 ISvcLocator *pSvcLocator)
123 return StatusCode::FAILURE;
129 return StatusCode::FAILURE;
143 auto magneticField = std::make_unique<ATLASMagneticFieldWrapper>();
147 detail::Navigator::Config
cfg{trackingGeometry};
148 cfg.resolvePassive =
false;
149 cfg.resolveMaterial =
true;
150 cfg.resolveSensitive =
true;
162 Acts::MeasurementSelectorCuts measurementSelectorCuts{
etaBins};
171 Acts::MeasurementSelector::Config measurementSelectorCfg{{Acts::GeometryIdentifier(), std::move(measurementSelectorCuts)}};
172 Acts::MeasurementSelector measurementSelector(measurementSelectorCfg);
174 std::vector<double> absEtaEdges;
175 absEtaEdges.reserve(
etaBins.size() + 2);
178 absEtaEdges.push_back(0.0);
179 absEtaEdges.push_back(std::numeric_limits<double>::infinity());
184 absEtaEdges.insert(absEtaEdges.end(),
etaBins.begin(),
etaBins.end());
188 auto setCut = [](
auto &cfgVal,
const auto &
cuts,
size_t ind) ->
void
195 Acts::TrackSelector::EtaBinnedConfig trackSelectorCfg{std::move(absEtaEdges)};
198 assert(trackSelectorCfg.cutSets.size() == 1);
199 trackSelectorCfg.cutSets[0].absEtaMin =
m_absEtaMin;
200 trackSelectorCfg.cutSets[0].absEtaMax =
m_absEtaMax;
203 for (
auto &
cfg : trackSelectorCfg.cutSets)
230 ATH_MSG_DEBUG(
"chi2OutlierCutOff set but not supported when using the default measurement selector.");
234 std::move(extrapolator),
235 {std::move(propagator),
logger().cloneWithSuffix(
"CKF")},
240 m_trackFinder = std::make_unique<CKF_pimpl>(std::move(ckfConfig));
242 trackFinder().
ckfExtensions.updater.connect<&ActsTrk::detail::FitterHelperFunctions::gainMatrixUpdate<detail::RecoTrackStateContainer>>();
246 return StatusCode::SUCCESS;
254 return StatusCode::SUCCESS;
272 std::vector<const ActsTrk::BoundTrackParametersContainer *> estimatedTrackParametersContainers;
276 ATH_MSG_DEBUG(
"Reading input collection with key " << estimatedTrackParametersKey.key());
279 estimatedTrackParametersContainers.push_back(estimatedTrackParametersHandle.
cptr());
280 ATH_MSG_DEBUG(
"Retrieved " << estimatedTrackParametersContainers.back()->size() <<
" input elements from key " << estimatedTrackParametersKey.key());
284 std::vector<const ActsTrk::SeedContainer *> seedContainers;
286 std::size_t total_seeds = 0;
289 ATH_MSG_DEBUG(
"Reading input collection with key " << seedContainerKey.key());
292 seedContainers.push_back(seedsHandle.
cptr());
293 ATH_MSG_DEBUG(
"Retrieved " << seedContainers.back()->size() <<
" input elements from key " << seedContainerKey.key());
294 total_seeds += seedContainers.back()->size();
298 std::vector<const xAOD::UncalibratedMeasurementContainer *> uncalibratedMeasurementContainers;
302 ATH_MSG_DEBUG(
"Reading input collection with key " << uncalibratedMeasurementContainerKey.key());
305 uncalibratedMeasurementContainers.push_back(uncalibratedMeasurementContainerHandle.
cptr());
306 ATH_MSG_DEBUG(
"Retrieved " << uncalibratedMeasurementContainers.back()->size() <<
" input elements from key " << uncalibratedMeasurementContainerKey.key());
311 ATH_CHECK(detectorElementToGeometryIdMap.isValid());
314 for (std::size_t icontainer = 0; icontainer < seedContainers.size(); ++icontainer)
316 if (!seedContainers[icontainer])
318 duplicateSeedDetector.
addSeeds(icontainer, *seedContainers[icontainer]);
322 const Acts::TrackingGeometry *
324 ATH_CHECK(acts_tracking_geometry !=
nullptr);
326 for (std::size_t icontainer = 0; icontainer < uncalibratedMeasurementContainers.size(); ++icontainer) {
329 *uncalibratedMeasurementContainers[icontainer],
330 **detectorElementToGeometryIdMap);
343 event_stat.resize(m_stat.size());
346 for (std::size_t icontainer = 0; icontainer < estimatedTrackParametersContainers.size(); ++icontainer)
348 if (estimatedTrackParametersContainers[icontainer]->
empty())
351 *acts_tracking_geometry,
352 **detectorElementToGeometryIdMap,
354 duplicateSeedDetector,
355 *estimatedTrackParametersContainers[icontainer],
356 seedContainers[icontainer],
363 ATH_MSG_DEBUG(
" \\__ Created " << tracksContainer.size() <<
" tracks");
365 mon_nTracks = tracksContainer.size();
377 ATH_CHECK(trackContainerHandle.record(std::move(constTracksContainer)));
378 if (!trackContainerHandle.isValid())
381 return StatusCode::FAILURE;
384 return StatusCode::SUCCESS;
391 const Acts::TrackingGeometry &trackingGeometry,
399 const char *seedType,
404 if (seeds && seeds->
size() != estimatedTrackParameters.
size())
407 ATH_MSG_WARNING(
"Have " << seeds->
size() <<
" " << seedType <<
" seeds, but " << estimatedTrackParameters.
size() <<
"estimated track parameters");
416 Acts::CalibrationContext calContext = Acts::CalibrationContext();
421 Acts::SourceLinkAccessorDelegate<detail::UncalibSourceLinkAccessor::Iterator> slAccessorDelegate;
424 Acts::PropagatorPlainOptions plainOptions{tgContext, mfContext};
425 Acts::PropagatorPlainOptions plainSecondOptions{tgContext, mfContext};
429 plainOptions.direction = reverseSearch ? Acts::Direction::Backward : Acts::Direction::Forward;
431 plainSecondOptions.direction = plainOptions.direction.invert();
434 using TrackFinderOptions = Acts::CombinatorialKalmanFilterOptions<detail::UncalibSourceLinkAccessor::Iterator, detail::RecoTrackContainer>;
435 TrackFinderOptions
options(tgContext,
442 if (reverseSearch)
options.targetSurface = pSurface.get();
446 std::optional<TrackFinderOptions> secondOptions;
448 secondOptions.emplace(tgContext,
455 if (!reverseSearch) secondOptions->targetSurface = pSurface.get();
459 secondOptions->skipPrePropagationUpdate =
true;
463 Acts::VectorTrackContainer trackBackend;
464 Acts::VectorMultiTrajectory trackStateBackend;
476 detectorElementToGeoId,
490 auto getCuts = [&trackSelectorCfg](
double eta) ->
const Acts::TrackSelector::Config & {
492 return (!(std::abs(eta) < trackSelectorCfg.absEtaEdges.back())) ? trackSelectorCfg.cutSets.back()
493 : (std::abs(eta) < trackSelectorCfg.absEtaEdges.front()) ? trackSelectorCfg.cutSets.front()
494 : trackSelectorCfg.getCuts(eta);
497 std::size_t category_i = 0;
500 using BranchStopperResult = Acts::CombinatorialKalmanFilterBranchStopperResult;
501 auto stopBranch = [&](
const detail::RecoTrackContainer::TrackProxy &
track,
502 const detail::RecoTrackContainer::TrackStateProxy &trackState) -> BranchStopperResult {
510 m_trackStatePrinter->printTrackState(tgContext, trackState, measurementContainerOffsets,
true);
514 return BranchStopperResult::Continue;
516 const auto &
parameters = trackState.hasFiltered() ? trackState.filtered() : trackState.predicted();
518 const auto &cutSet = getCuts(eta);
527 <<
track.nMeasurements() <<
" measurements");
528 return BranchStopperResult::StopAndDrop;
538 <<
track.nMeasurements() <<
" measurements");
539 return BranchStopperResult::StopAndDrop;
542 bool enoughMeasurements = (
track.nMeasurements() >= cutSet.minMeasurements);
543 bool tooManyHoles = (
track.nHoles() > cutSet.maxHoles);
544 bool tooManyOutliers = (
track.nOutliers() > cutSet.maxOutliers);
548 enoughMeasurements = enoughMeasurements && enoughMeasurementsPS;
549 tooManyHoles = tooManyHoles || tooManyHolesPS;
550 tooManyOutliers = tooManyOutliers || tooManyOutliersPS;
553 if (!(tooManyHoles || tooManyOutliers))
554 return BranchStopperResult::Continue;
556 if (!enoughMeasurements)
560 << (enoughMeasurements ?
"keep" :
"drop")
561 <<
" branch with nHoles=" <<
track.nHoles()
564 <<
" strip), nOutliers=" <<
track.nOutliers()
567 <<
"), nMeasurements=" <<
track.nMeasurements()
573 << (enoughMeasurements ?
"keep" :
"drop")
574 <<
" branch with nHoles=" <<
track.nHoles()
575 <<
", nOutliers=" <<
track.nOutliers()
576 <<
", nMeasurements=" <<
track.nMeasurements());
579 return enoughMeasurements ? BranchStopperResult::StopAndKeep
580 : BranchStopperResult::StopAndDrop;
583 options.extensions.branchStopper.connect(stopBranch);
589 secondOptions->extensions.branchStopper.connect(stopBranch);
593 Acts::ActorList<Acts::MaterialInteractor>>
594 extrapolationOptions(tgContext, mfContext);
596 Acts::TrackExtrapolationStrategy extrapolationStrategy =
600 ATH_MSG_DEBUG(
"Invoke track finding with " << estimatedTrackParameters.
size() <<
' ' << seedType <<
" seeds.");
602 std::size_t nPrinted = 0;
603 auto printSeed = [&](std::size_t iseed,
const Acts::BoundTrackParameters &seedParameters,
bool isKF =
false)
609 ATH_MSG_INFO(
"CKF results for " << estimatedTrackParameters.
size() <<
' ' << seedType <<
" seeds:");
611 m_trackStatePrinter->printSeed(tgContext, *(*seeds)[iseed], seedParameters, measurementContainerOffsets, iseed, isKF);
615 for (std::size_t iseed = 0; iseed < estimatedTrackParameters.
size(); ++iseed)
618 tracksContainerTemp.clear();
620 if (!estimatedTrackParameters[iseed])
627 const Acts::BoundTrackParameters *initialParameters = estimatedTrackParameters[iseed];
628 printSeed(iseed, *initialParameters);
634 if (duplicateSeedDetector.
isDuplicate(typeIndex, iseed))
636 ATH_MSG_DEBUG(
"skip " << seedType <<
" seed " << iseed <<
" - already found");
645 std::unique_ptr<Acts::BoundTrackParameters> seedParameters;
650 const auto fittedSeedCollection =
m_fitterTool->fit(ctx, *(*seeds)[iseed], *initialParameters,
651 tgContext, mfContext, calContext,
652 detectorElementToGeoId);
653 if (not fittedSeedCollection)
657 else if (fittedSeedCollection->size() != 1)
659 ATH_MSG_WARNING(
"KF produced " << fittedSeedCollection->size() <<
" tracks but should produce 1!");
664 const auto fittedSeed = fittedSeedCollection->getTrack(0);
666 double etaSeed = -
std::log(
std::tan(0.5 * fittedSeed.parameters()[Acts::eBoundTheta]));
667 const auto &cutSet = getCuts(etaSeed);
668 if (fittedSeed.transverseMomentum() < cutSet.ptMin)
670 ATH_MSG_VERBOSE(
"min pt requirement not satisfied after param refinement: pt min is " << cutSet.ptMin <<
" but Refined params have pt of " << fittedSeed.transverseMomentum());
675 seedParameters.reset(
new Acts::BoundTrackParameters(fittedSeed.referenceSurface().getSharedPtr(),
676 fittedSeed.parameters(),
677 initialParameters->covariance(),
678 fittedSeed.particleHypothesis()));
679 printSeed(iseed, *seedParameters,
true);
682 initialParameters = seedParameters.get();
690 ATH_MSG_WARNING(
"Track finding failed for " << seedType <<
" seed " << iseed <<
" with error" <<
result.error());
693 auto &tracksForSeed =
result.value();
702 if (!
track.hasReferenceSurface()) {
703 auto extrapolationResult = Acts::extrapolateTrackToReferenceSurface(
705 extrapolationStrategy,
logger());
706 if (!extrapolationResult.ok()) {
708 << iseed <<
" and " <<
track.index()
709 <<
" failed with error " << extrapolationResult.error()
710 <<
" dropping track candidate.");
715 Acts::trimTrack(
track,
true,
true,
true);
716 Acts::calculateTrackQuantities(
track);
719 for (
const auto& trackState :
track.trackStatesReversed()) {
733 auto selectPixelStripCountsFinal = [
this](
const detail::RecoTrackContainer::TrackProxy &
track) {
737 return enoughMeasurementsPS && !tooManyHolesPS && !tooManyOutliersPS;
740 selectPixelStripCountsFinal(
track)) {
741 auto destProxy = tracksContainer.getTrack(tracksContainer.addTrack());
742 destProxy.copyFrom(
track,
true);
750 ATH_MSG_DEBUG(
"Track " << ntracks <<
" from " << seedType <<
" seed " << iseed <<
" failed track selection");
754 std::size_t nfirst = 0;
755 for (
auto &firstTrack : tracksForSeed) {
756 std::size_t nsecond = 0;
758 auto smoothingResult = Acts::smoothTrack(tgContext, firstTrack,
logger(), Acts::MbfSmoother());
759 if (!smoothingResult.ok()) {
761 << iseed <<
" and first track " << firstTrack.index()
762 <<
" failed with error " << smoothingResult.error());
767 std::optional<detail::RecoTrackStateContainerProxy> firstMeasurement;
768 for (
auto st : firstTrack.trackStatesReversed()) {
769 bool isMeasurement = st.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag);
770 bool isOutlier = st.typeFlags().test(Acts::TrackStateFlag::OutlierFlag);
774 if (isMeasurement && !isOutlier)
775 firstMeasurement = st;
778 if (firstMeasurement.has_value()) {
779 Acts::BoundTrackParameters secondInitialParameters = firstTrack.createParametersFromState(*firstMeasurement);
781 if (!secondInitialParameters.referenceSurface().insideBounds(secondInitialParameters.localPosition())) {
782 ATH_MSG_DEBUG(
"Smoothing of first pass fit produced out-of-bounds parameters relative to the surface, '"
783 << secondInitialParameters.referenceSurface().name()
784 <<
"'. Skipping second pass for " << seedType <<
" seed " << iseed <<
" track " << nfirst);
786 auto rootBranch = tracksContainerTemp.makeTrack();
787 rootBranch.copyFrom(firstTrack,
false);
790 auto secondResult =
trackFinder().
ckf.findTracks(secondInitialParameters, *secondOptions, tracksContainerTemp, rootBranch);
792 if (not secondResult.ok()) {
793 ATH_MSG_WARNING(
"Second track finding failed for " << seedType <<
" seed " << iseed <<
" track " << nfirst <<
" with error" << secondResult.error());
796 auto originalFirstMeasurementPrevious = firstMeasurement->previous();
798 auto &secondTracksForSeed = secondResult.value();
799 for (
auto &secondTrack : secondTracksForSeed) {
800 secondTrack.reverseTrackStates(
true);
802 firstMeasurement->previous() = secondTrack.outermostTrackState().index();
803 secondTrack.tipIndex() = firstTrack.tipIndex();
807 auto secondSmoothingResult = Acts::smoothTrack(tgContext, secondTrack,
logger());
808 if (!secondSmoothingResult.ok()) {
809 ATH_MSG_WARNING(
"Second smoothing for seed " << iseed <<
" and track " << secondTrack.index() <<
" failed with error " << secondSmoothingResult.error());
813 secondTrack.reverseTrackStates(
true);
822 firstMeasurement->previous() = originalFirstMeasurementPrevious;
829 ATH_MSG_DEBUG(
"No viable result from second track finding for " << seedType <<
" seed " << iseed <<
" track " << nfirst);
838 ATH_MSG_DEBUG(
"Track finding found no track candidates for " << seedType <<
" seed " << iseed);
840 }
else if (ntracks >= 2) {
849 return StatusCode::SUCCESS;
857 const auto lastMeasurementIndex =
track.tipIndex();
860 tracksContainer.trackStateContainer().visitBackwards(
861 lastMeasurementIndex,
862 [&duplicateSeedDetector](
const detail::RecoTrackStateContainer::ConstTrackStateProxy &state) ->
void
865 if (not state.hasUncalibratedSourceLink())
869 auto sl = state.getUncalibratedSourceLink().template get<ATLASUncalibSourceLink>();
877 if (trackState.hasReferenceSurface()) {
878 if (
const auto *actsDetElem =
dynamic_cast<const ActsDetectorElement *
>(trackState.referenceSurface().associatedDetectorElement())) {
880 if (detElem->isPixel()) {
882 }
else if (detElem->isSCT()) {
894 tracksContainer.addColumn<
unsigned int>(
"nPixelHits");
895 tracksContainer.addColumn<
unsigned int>(
"nStripHits");
896 tracksContainer.addColumn<
unsigned int>(
"nPixelHoles");
897 tracksContainer.addColumn<
unsigned int>(
"nStripHoles");
898 tracksContainer.addColumn<
unsigned int>(
"nPixelOutliers");
899 tracksContainer.addColumn<
unsigned int>(
"nStripOutliers");
915 Acts::ConstTrackStateType typeFlags,
919 if (typeFlags.test(Acts::TrackStateFlag::HoleFlag)) {
921 }
else if (typeFlags.test(Acts::TrackStateFlag::OutlierFlag)) {
923 }
else if (typeFlags.test(Acts::TrackStateFlag::MeasurementFlag)) {
927 if (typeFlags.test(Acts::TrackStateFlag::HoleFlag)) {
929 }
else if (typeFlags.test(Acts::TrackStateFlag::OutlierFlag)) {
931 }
else if (typeFlags.test(Acts::TrackStateFlag::MeasurementFlag)) {
939 const detail::RecoTrackContainer::TrackProxy &
other)
971 bool enoughMeasurements =
true, tooManyHoles =
false, tooManyOutliers =
false;
973 std::size_t
etaBin = (std::abs(eta) < trackSelectorCfg.absEtaEdges.front()) ? 0
974 : (std::abs(eta) >= trackSelectorCfg.absEtaEdges.back()) ? trackSelectorCfg.absEtaEdges.size() - 1
975 : trackSelectorCfg.binIndex(eta);
976 auto cutMin = [
etaBin](std::size_t
val,
const std::vector<std::size_t> &cutSet) {
977 return !cutSet.empty() && (
val < (
etaBin < cutSet.size() ? cutSet[
etaBin] : cutSet.back()));
979 auto cutMax = [
etaBin](std::size_t
val,
const std::vector<std::size_t> &cutSet) {
980 return !cutSet.empty() && (
val > (
etaBin < cutSet.size() ? cutSet[
etaBin] : cutSet.back()));
988 return {enoughMeasurements, tooManyHoles, tooManyOutliers};
1003 ATH_MSG_FATAL(
"Eta bins for statistics counter not in ascending order.");
1014 std::lock_guard<std::mutex> lock(m_mutex);
1015 std::size_t category_i = 0;
1016 for (
const std::array<unsigned int, kNStat> &src_stat : event_stat)
1018 std::array<std::size_t, kNStat> &dest_stat = m_stat[category_i++];
1019 for (std::size_t
i = 0;
i < src_stat.size(); ++
i)
1021 assert(
i < dest_stat.size());
1022 dest_stat[
i] += src_stat[
i];
1032 std::vector<std::string> stat_labels =
1038 std::make_pair(
kNoTrack,
"Cannot find track"),
1045 std::make_pair(
kNoSecond,
"Tracks failing second CKF"),
1049 assert(stat_labels.size() ==
kNStat);
1055 std::vector<std::string> eta_labels;
1057 for (std::size_t eta_bin_i = 0; eta_bin_i <
m_statEtaBins.size() + 2; ++eta_bin_i)
1066 std::vector<std::size_t>
stat =
1067 TableUtils::createCounterArrayWithProjections<std::size_t>(
nSeedCollections(),
1072 std::size_t stat_stride =
1076 std::size_t eta_stride =
1080 std::stringstream table_out;
1086 for (std::size_t stat_i = 0; stat_i <
kNStat; ++stat_i)
1088 std::size_t dest_idx_offset = stat_i * stat_stride;
1094 .dumpHeader(stat_i == 0)
1095 .dumpFooter(stat_i + 1 ==
kNStat)
1096 .separateLastRow(
true)
1097 .minLabelWidth(max_label_width)
1098 .labelPrefix(stat_labels.at(stat_i));
1108 std::size_t dest_idx_offset = eta_bin_i * eta_stride;
1112 eta_labels.at(eta_bin_i))
1119 << table_out.str());
1125 auto [ratio_labels, ratio_def] =
1127 std::vector<TableUtils::SummandDefinition>{
1159 for (std::size_t ratio_i = 0; ratio_i < ratio_labels.size(); ++ratio_i)
1162 ratio_i * ratio_stride,
1168 .dumpHeader(ratio_i == 0)
1169 .dumpFooter(ratio_i + 1 == ratio_labels.size())
1170 .separateLastRow(
true)
1171 .minLabelWidth(max_label_width)
1172 .labelPrefix(ratio_labels.at(ratio_i));
1179 (
m_statEtaBins.size() + 1) * ratio_eta_stride + 0 * ratio_stride,
1185 .minLabelWidth(max_label_width)
1189 eta_labels.erase(eta_labels.end() - 1);
1190 constexpr std::size_t ratio_i = 3;
1192 ratio_i * ratio_stride,
1200 .separateLastRow(
false)
1201 .minLabelWidth(max_label_width)
1202 .labelPrefix(ratio_labels.at(ratio_i));
1206 << table_out.str());
1212 std::vector<float>::const_iterator bin_iter = std::upper_bound(
m_statEtaBins.begin(),
1216 assert(category_i < m_stat.size());
1222 std::size_t
out = 0
u;
1223 for (std::size_t category_i = seed_collection *
seedCollectionStride() +
static_cast<std::size_t
>(counter_i);
1227 assert(category_i <
stat.size());
1228 out +=
stat[category_i][counter_i];
1235 std::vector<std::pair<float, float> > chi2CutOffOutlier ;
1239 ATH_MSG_ERROR(
"Outlier chi2 cut off provided but number of elements does not agree with"
1240 " chi2 cut off for measurements which however is required: "
1242 return StatusCode::FAILURE;
1247 chi2CutOffOutlier.push_back( std::make_pair(
static_cast<float>(elm),
1253 std::vector<float> etaBinsf;