From 27f680e3511683a26a174fc4aee4aceabdeeae19 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Wed, 5 Apr 2023 14:05:25 +0200 Subject: [PATCH] feat: Track level improvements (#2012) -- squash commit 98355e0039d024ab1efa7166ea4dff5b1787360e Author: Paul Gessinger Date: Tue Apr 4 15:04:58 2023 +0200 doxygen commit a162b2d94243d93126e79b50fd114733476fe1e8 Author: Paul Gessinger Date: Mon Apr 3 16:25:25 2023 +0200 fix wrong function commit 6021ea2d4a887a7d4a5df1a161fb6887e76ad7ee Author: Paul Gessinger Date: Mon Apr 3 16:13:33 2023 +0200 unit test, append track state functionality commit 9a25c0a764ea4fddf6a028fffe67aef5ec873d7f Author: Paul Gessinger Date: Mon Apr 3 15:58:43 2023 +0200 some basic unit testing commit 83aa3e9ea9c3d969122ed8d54a3fb846040fc62d Author: Paul Gessinger Date: Mon Apr 3 14:35:38 2023 +0200 feat: Fitters calculate track quantities Adds a central helper function that calculates number of holes, outliers, measurements and shared hits based on the type flags, and stores them in the track. commit 2235f829fe30340bf1e8f34c98bbdecc42dd29d5 Author: Paul Gessinger Date: Mon Apr 3 14:21:25 2023 +0200 refactor: Changce TrackProxy chi2 method name commit aa6bc1d4ab1e6a016f3bc2043316085dfbaf55e1 Author: Paul Gessinger Date: Fri Mar 31 14:31:08 2023 +0200 add ndf and chi2 to track container --- Core/include/Acts/EventData/TrackHelpers.hpp | 53 ++++++++++++++ Core/include/Acts/EventData/TrackProxy.hpp | 68 +++++++++++++++++ .../Acts/EventData/VectorTrackContainer.hpp | 20 +++++ .../CombinatorialKalmanFilter.hpp | 4 + Core/include/Acts/TrackFitting/Chi2Fitter.hpp | 3 + .../Acts/TrackFitting/GaussianSumFitter.hpp | 3 + .../Acts/TrackFitting/KalmanFitter.hpp | 7 ++ Core/src/EventData/VectorTrackContainer.cpp | 24 +++++- Tests/UnitTests/Core/EventData/TrackTests.cpp | 73 +++++++++++++++++++ 9 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 Core/include/Acts/EventData/TrackHelpers.hpp diff --git a/Core/include/Acts/EventData/TrackHelpers.hpp b/Core/include/Acts/EventData/TrackHelpers.hpp new file mode 100644 index 000000000000..cbeeddea7169 --- /dev/null +++ b/Core/include/Acts/EventData/TrackHelpers.hpp @@ -0,0 +1,53 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 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 http://mozilla.org/MPL/2.0/. +#pragma once + +#include "Acts/EventData/MultiTrajectory.hpp" +#include "Acts/EventData/TrackContainer.hpp" + +namespace Acts { + +/// Helper function to calculate a number of track level quantities and store +/// them on the track itself +/// @tparam track_container_t the track container backend +/// @tparam track_state_container_t the track state container backend +/// @tparam holder_t the holder type for the track container backends +/// @param track A track proxy to operate on +template class holder_t> +void calculateTrackQuantities( + Acts::TrackProxy + track) { + track.chi2() = 0; + track.nDoF() = 0; + + track.nHoles() = 0; + track.nMeasurements() = 0; + track.nSharedHits() = 0; + track.nOutliers() = 0; + + for (const auto& trackState : track.trackStates()) { + auto typeFlags = trackState.typeFlags(); + + if (typeFlags.test(Acts::TrackStateFlag::MeasurementFlag)) { + if (typeFlags.test(Acts::TrackStateFlag::SharedHitFlag)) { + track.nSharedHits()++; + } + + track.nMeasurements()++; + track.chi2() += trackState.chi2(); + track.nDoF() += trackState.calibratedSize(); + } else if (typeFlags.test(Acts::TrackStateFlag::OutlierFlag)) { + track.nOutliers()++; + } else if (typeFlags.test(Acts::TrackStateFlag::HoleFlag)) { + track.nHoles()++; + } + } +} +} // namespace Acts diff --git a/Core/include/Acts/EventData/TrackProxy.hpp b/Core/include/Acts/EventData/TrackProxy.hpp index 9e75fe785262..55ad4fa67f9c 100644 --- a/Core/include/Acts/EventData/TrackProxy.hpp +++ b/Core/include/Acts/EventData/TrackProxy.hpp @@ -11,6 +11,7 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/EventData/Charge.hpp" #include "Acts/EventData/MultiTrajectory.hpp" +#include "Acts/EventData/TrackStatePropMask.hpp" #include "Acts/Utilities/UnitVectors.hpp" #include @@ -385,6 +386,19 @@ class TrackProxy { return m_container->trackStateRange(m_index); } + /// Append a track state to this track. This will modify the tip index to + /// point at the newly created track state, which will be directly after the + /// previous track state at tip index. + /// @param mask The allocation prop mask for the new track state + /// @return The newly added track state + template > + auto appendTrackState(TrackStatePropMask mask = TrackStatePropMask::All) { + auto& tsc = m_container->trackStateContainer(); + auto ts = tsc.getTrackState(tsc.addTrackState(mask, tipIndex())); + tipIndex() = ts.index(); + return ts; + } + /// Return the number of track states associated to this track /// @note This is calculated by iterating over the track states which is /// somewhat expensive. Consider caching this value if you need It @@ -429,6 +443,60 @@ class TrackProxy { return component(hashString("nHoles")); } + /// Return a mutable reference to the number of outliers for the track. + /// Mutable version + /// @return The number of outliers + template > + unsigned int& nOutliers() { + return component(hashString("nOutliers")); + } + + /// Return the number of outliers for the track. Const version + /// @return The number of outliers + unsigned int nOutliers() const { + return component(hashString("nOutliers")); + } + + /// Return a mutable reference to the number of shared hits for the track. + /// Mutable version + /// @return The number of shared hits + template > + unsigned int& nSharedHits() { + return component(hashString("nSharedHits")); + } + + /// Return the number of shared hits for the track. Const version + /// @return The number of shared hits + unsigned int nSharedHits() const { + return component(hashString("nSharedHits")); + } + + /// Return a mutable reference to the chi squared + /// Mutable version + /// @return The chi squared + template > + float& chi2() { + return component(hashString("chi2")); + } + + /// Return the chi squared for the track. Const version + /// @return The chi squared + float chi2() const { return component(hashString("chi2")); } + + /// Return a mutable reference to the number of degrees of freedom for the + /// track. Mutable version + /// @return The the number of degrees of freedom + template > + unsigned int& nDoF() { + return component(hashString("ndf")); + } + + /// Return the number of degrees of freedom for the track. Const version + /// @return The number of degrees of freedom + unsigned int nDoF() const { + return component(hashString("ndf")); + } + /// Return the index of this track in the track container /// @note This is separate from the tip index /// @return the track index diff --git a/Core/include/Acts/EventData/VectorTrackContainer.hpp b/Core/include/Acts/EventData/VectorTrackContainer.hpp index d36bbe851a0b..6cc37602c730 100644 --- a/Core/include/Acts/EventData/VectorTrackContainer.hpp +++ b/Core/include/Acts/EventData/VectorTrackContainer.hpp @@ -67,6 +67,14 @@ class VectorTrackContainerBase { return &instance.m_nMeasurements[itrack]; case "nHoles"_hash: return &instance.m_nHoles[itrack]; + case "chi2"_hash: + return &instance.m_chi2[itrack]; + case "ndf"_hash: + return &instance.m_ndf[itrack]; + case "nOutliers"_hash: + return &instance.m_nOutliers[itrack]; + case "nSharedHits"_hash: + return &instance.m_nSharedHits[itrack]; default: auto it = instance.m_dynamic.find(key); if (it == instance.m_dynamic.end()) { @@ -97,6 +105,14 @@ class VectorTrackContainerBase { result = result && m_nMeasurements.size() == size; assert(result); result = result && m_nHoles.size() == size; + assert(result); + result = result && m_chi2.size() == size; + assert(result); + result = result && m_ndf.size() == size; + assert(result); + result = result && m_nOutliers.size() == size; + assert(result); + result = result && m_nSharedHits.size() == size; for (const auto& [key, col] : m_dynamic) { (void)key; @@ -128,6 +144,10 @@ class VectorTrackContainerBase { std::vector m_nMeasurements; std::vector m_nHoles; + std::vector m_chi2; + std::vector m_ndf; + std::vector m_nOutliers; + std::vector m_nSharedHits; std::unordered_map> m_dynamic; diff --git a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp index 58d58d1de40e..a273c5ed7b6b 100644 --- a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp +++ b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp @@ -18,6 +18,7 @@ #include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/MultiTrajectoryHelpers.hpp" #include "Acts/EventData/TrackContainer.hpp" +#include "Acts/EventData/TrackHelpers.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/EventData/TrackStatePropMask.hpp" #include "Acts/Geometry/GeometryContext.hpp" @@ -1379,6 +1380,9 @@ class CombinatorialKalmanFilter { track.parameters() = parameters.parameters(); track.covariance() = *parameters.covariance(); track.setReferenceSurface(parameters.referenceSurface().getSharedPtr()); + + calculateTrackQuantities(track); + tracks.push_back(track); } diff --git a/Core/include/Acts/TrackFitting/Chi2Fitter.hpp b/Core/include/Acts/TrackFitting/Chi2Fitter.hpp index a120710e3eec..0cef1892379f 100644 --- a/Core/include/Acts/TrackFitting/Chi2Fitter.hpp +++ b/Core/include/Acts/TrackFitting/Chi2Fitter.hpp @@ -18,6 +18,7 @@ #include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/MultiTrajectoryHelpers.hpp" #include "Acts/EventData/SourceLink.hpp" +#include "Acts/EventData/TrackHelpers.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/EventData/VectorMultiTrajectory.hpp" #include "Acts/Geometry/GeometryContext.hpp" @@ -791,6 +792,8 @@ class Chi2Fitter { track.nMeasurements() = c2r.measurementStates; track.nHoles() = c2r.measurementHoles; + calculateTrackQuantities(track); + if (trackContainer.hasColumn(hashString("chi2"))) { track.template component() = c2r.chisquare; diff --git a/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp b/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp index e0df85d8ad24..b4b1b21b0a2e 100644 --- a/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp +++ b/Core/include/Acts/TrackFitting/GaussianSumFitter.hpp @@ -8,6 +8,7 @@ #pragma once +#include "Acts/EventData/TrackHelpers.hpp" #include "Acts/EventData/VectorMultiTrajectory.hpp" #include "Acts/Propagator/EigenStepper.hpp" #include "Acts/Propagator/MultiStepperAborters.hpp" @@ -442,6 +443,8 @@ struct GaussianSumFitter { track.setReferenceSurface(params.referenceSurface().getSharedPtr()); } + calculateTrackQuantities(track); + track.nMeasurements() = measurementStatesFinal; track.nHoles() = fwdGsfResult.measurementHoles; diff --git a/Core/include/Acts/TrackFitting/KalmanFitter.hpp b/Core/include/Acts/TrackFitting/KalmanFitter.hpp index 6bceb1d01b51..eb4445adfdc2 100644 --- a/Core/include/Acts/TrackFitting/KalmanFitter.hpp +++ b/Core/include/Acts/TrackFitting/KalmanFitter.hpp @@ -17,6 +17,7 @@ #include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/MultiTrajectoryHelpers.hpp" #include "Acts/EventData/SourceLink.hpp" +#include "Acts/EventData/TrackHelpers.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/EventData/VectorMultiTrajectory.hpp" #include "Acts/Geometry/GeometryContext.hpp" @@ -1126,6 +1127,9 @@ class KalmanFitter { } track.nMeasurements() = kalmanResult.measurementStates; track.nHoles() = kalmanResult.measurementHoles; + + calculateTrackQuantities(track); + if (trackContainer.hasColumn(hashString("smoothed"))) { track.template component() = kalmanResult.smoothed; @@ -1259,6 +1263,9 @@ class KalmanFitter { } track.nMeasurements() = kalmanResult.measurementStates; track.nHoles() = kalmanResult.measurementHoles; + + calculateTrackQuantities(track); + if (trackContainer.hasColumn(hashString("smoothed"))) { track.template component() = kalmanResult.smoothed; diff --git a/Core/src/EventData/VectorTrackContainer.cpp b/Core/src/EventData/VectorTrackContainer.cpp index 96e4a6ef4d3b..8060a95a2222 100644 --- a/Core/src/EventData/VectorTrackContainer.cpp +++ b/Core/src/EventData/VectorTrackContainer.cpp @@ -19,7 +19,11 @@ VectorTrackContainerBase::VectorTrackContainerBase( m_cov{other.m_cov}, m_referenceSurfaces{other.m_referenceSurfaces}, m_nMeasurements{other.m_nMeasurements}, - m_nHoles{other.m_nHoles} { + m_nHoles{other.m_nHoles}, + m_chi2{other.m_chi2}, + m_ndf{other.m_ndf}, + m_nOutliers{other.m_nOutliers}, + m_nSharedHits{other.m_nSharedHits} { for (const auto& [key, value] : other.m_dynamic) { m_dynamic.insert({key, value->clone()}); } @@ -39,6 +43,12 @@ VectorTrackContainer::IndexType VectorTrackContainer::addTrack_impl() { m_nMeasurements.emplace_back(); m_nHoles.emplace_back(); + m_chi2.emplace_back(); + m_ndf.emplace_back(); + + m_nOutliers.emplace_back(); + m_nSharedHits.emplace_back(); + // dynamic columns for (auto& [key, vec] : m_dynamic) { vec->add(); @@ -66,6 +76,12 @@ void VectorTrackContainer::removeTrack_impl(IndexType itrack) { erase(m_nMeasurements); erase(m_nHoles); + erase(m_chi2); + erase(m_ndf); + + erase(m_nOutliers); + erase(m_nSharedHits); + for (auto& [key, vec] : m_dynamic) { vec->erase(itrack); } @@ -103,6 +119,12 @@ void VectorTrackContainer::reserve(IndexType size) { m_nMeasurements.reserve(size); m_nHoles.reserve(size); + m_chi2.reserve(size); + m_ndf.reserve(size); + + m_nOutliers.reserve(size); + m_nSharedHits.reserve(size); + for (auto& [key, vec] : m_dynamic) { vec->reserve(size); } diff --git a/Tests/UnitTests/Core/EventData/TrackTests.cpp b/Tests/UnitTests/Core/EventData/TrackTests.cpp index 78d73ffc2b32..8ba0c07a3281 100644 --- a/Tests/UnitTests/Core/EventData/TrackTests.cpp +++ b/Tests/UnitTests/Core/EventData/TrackTests.cpp @@ -12,6 +12,7 @@ #include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/TrackContainer.hpp" +#include "Acts/EventData/TrackHelpers.hpp" #include "Acts/EventData/TrackStatePropMask.hpp" #include "Acts/EventData/VectorMultiTrajectory.hpp" #include "Acts/EventData/VectorTrackContainer.hpp" @@ -240,6 +241,18 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(Build, factory_t, holder_types) { t2.nHoles() = 67; BOOST_CHECK_EQUAL(t.nHoles(), 67); + t2.nOutliers() = 68; + BOOST_CHECK_EQUAL(t.nOutliers(), 68); + + t2.nSharedHits() = 69; + BOOST_CHECK_EQUAL(t.nSharedHits(), 69); + + t2.chi2() = 555.0; + BOOST_CHECK_EQUAL(t2.chi2(), 555.0); + + t2.nDoF() = 123; + BOOST_CHECK_EQUAL(t2.nDoF(), 123); + // const checks: should not compile // const auto& ctc = tc; // ctc.getTrack(idx).covariance().setRandom(); @@ -531,4 +544,64 @@ BOOST_AUTO_TEST_CASE(EnsureDynamicColumns) { BOOST_CHECK(tc2.hasColumn("odd")); } +BOOST_AUTO_TEST_CASE(AppendTrackState) { + TrackContainer tc{VectorTrackContainer{}, VectorMultiTrajectory{}}; + auto t = tc.getTrack(tc.addTrack()); + + std::vector::TrackStateProxy> + trackStates; + trackStates.push_back(t.appendTrackState()); + trackStates.push_back(t.appendTrackState()); + trackStates.push_back(t.appendTrackState()); + trackStates.push_back(t.appendTrackState()); + trackStates.push_back(t.appendTrackState()); + trackStates.push_back(t.appendTrackState()); + + BOOST_CHECK_EQUAL(trackStates.size(), t.nTrackStates()); + + for (size_t i = trackStates.size() - 1; i > 0; i--) { + BOOST_CHECK_EQUAL(trackStates.at(i).index(), i); + } +} + +BOOST_AUTO_TEST_CASE(CalculateQuantities) { + TrackContainer tc{VectorTrackContainer{}, VectorMultiTrajectory{}}; + auto t = tc.getTrack(tc.addTrack()); + + // std::vector< + + auto ts = t.appendTrackState(); + ts.typeFlags().set(MeasurementFlag); + + ts = t.appendTrackState(); + ts.typeFlags().set(OutlierFlag); + + ts = t.appendTrackState(); + ts.typeFlags().set(MeasurementFlag); + ts.typeFlags().set(SharedHitFlag); + + ts = t.appendTrackState(); + ts.typeFlags().set(HoleFlag); + + ts = t.appendTrackState(); + ts.typeFlags().set(OutlierFlag); + + ts = t.appendTrackState(); + ts.typeFlags().set(HoleFlag); + + ts = t.appendTrackState(); + ts.typeFlags().set(MeasurementFlag); + ts.typeFlags().set(SharedHitFlag); + + ts = t.appendTrackState(); + ts.typeFlags().set(OutlierFlag); + + calculateTrackQuantities(t); + + BOOST_CHECK_EQUAL(t.nHoles(), 2); + BOOST_CHECK_EQUAL(t.nMeasurements(), 3); + BOOST_CHECK_EQUAL(t.nOutliers(), 3); + BOOST_CHECK_EQUAL(t.nSharedHits(), 2); +} + BOOST_AUTO_TEST_SUITE_END()