diff --git a/PhysicsAnalysis/NtupleDumper/CMakeLists.txt b/PhysicsAnalysis/NtupleDumper/CMakeLists.txt index 607c58d892d201731098f64829a82d556f1a302a..dd8faf032f48b2c3d78453b84494e1903d64367d 100644 --- a/PhysicsAnalysis/NtupleDumper/CMakeLists.txt +++ b/PhysicsAnalysis/NtupleDumper/CMakeLists.txt @@ -7,7 +7,7 @@ atlas_add_component( src/NtupleDumperAlg.h src/NtupleDumperAlg.cxx src/component/NtupleDumper_entries.cxx - LINK_LIBRARIES AthenaBaseComps StoreGateLib xAODFaserWaveform xAODFaserCalorimeter xAODFaserTrigger xAODFaserLHC ScintIdentifier FaserCaloIdentifier GeneratorObjects FaserActsGeometryLib TrackerSimEvent TrackerSimData TrackerIdentifier TrackerReadoutGeometry TrkTrack GeoPrimitives TrackerRIO_OnTrack TrackerSpacePoint FaserActsKalmanFilterLib + LINK_LIBRARIES AthenaBaseComps StoreGateLib xAODFaserWaveform xAODFaserCalorimeter xAODFaserTrigger xAODFaserLHC ScintIdentifier FaserCaloIdentifier GeneratorObjects FaserActsGeometryLib TrackerSimEvent TrackerSimData TrackerIdentifier TrackerReadoutGeometry TrkTrack GeoPrimitives TrackerRIO_OnTrack TrackerSpacePoint FaserActsKalmanFilterLib FaserActsmanVertexingLib PRIVATE_LINK_LIBRARIES nlohmann_json::nlohmann_json ) diff --git a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx index 6e4c2aae531c6feee89df6102a90d118f6276dff..1568097245d10680bb9a9889b13ceb5b854f9e51 100644 --- a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx +++ b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx @@ -17,7 +17,9 @@ #include <cmath> #include <TH1F.h> #include <numeric> +#include <limits> +constexpr double NaN = std::numeric_limits<double>::quiet_NaN(); using namespace Acts::UnitLiterals; NtupleDumperAlg::NtupleDumperAlg(const std::string &name, @@ -119,6 +121,7 @@ StatusCode NtupleDumperAlg::initialize() ATH_CHECK(m_trackingGeometryTool.retrieve()); ATH_CHECK(m_trackTruthMatchingTool.retrieve()); ATH_CHECK(m_fiducialParticleTool.retrieve()); + ATH_CHECK(m_vertexingTool.retrieve()); ATH_CHECK(m_spacePointContainerKey.initialize()); @@ -289,6 +292,8 @@ StatusCode NtupleDumperAlg::initialize() //TRUTH m_tree->Branch("t_pdg", &m_t_pdg); m_tree->Branch("t_barcode", &m_t_barcode); + m_tree->Branch("t_pdg_parent", &m_t_pdg_parent); + m_tree->Branch("t_barcode_parent", &m_t_barcode_parent); m_tree->Branch("t_truthHitRatio", &m_t_truthHitRatio); m_tree->Branch("t_prodVtx_x", &m_t_prodVtx_x); @@ -375,6 +380,19 @@ StatusCode NtupleDumperAlg::initialize() m_tree->Branch("truthd1_y", &m_truthd1_y); m_tree->Branch("truthd1_z", &m_truthd1_z); + m_tree->Branch("vertex_x", &m_vertex_x, "vertex_x/D"); + m_tree->Branch("vertex_y", &m_vertex_y, "vertex_y/D"); + m_tree->Branch("vertex_z", &m_vertex_z, "vertex_z/D"); + m_tree->Branch("vertex_chi2", &m_vertex_chi2, "vertex_chi2/D"); + m_tree->Branch("vertex_px0", &m_vertex_px0, "vertex_px0/D"); + m_tree->Branch("vertex_py0", &m_vertex_py0, "vertex_py0/D"); + m_tree->Branch("vertex_pz0", &m_vertex_pz0, "vertex_pz0/D"); + m_tree->Branch("vertex_p0", &m_vertex_p0, "vertex_p0/D"); + m_tree->Branch("vertex_px1", &m_vertex_px1, "vertex_px1/D"); + m_tree->Branch("vertex_py1", &m_vertex_py1, "vertex_py1/D"); + m_tree->Branch("vertex_pz1", &m_vertex_pz1, "vertex_pz1/D"); + m_tree->Branch("vertex_p1", &m_vertex_p1, "vertex_p1/D"); + ATH_CHECK(histSvc()->regTree("/HIST2/tree", m_tree)); // Register histograms @@ -885,6 +903,8 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const } ATH_CHECK(trackCollection.isValid()); + // create list of good tracks in fiducial region (r < 95mm) and chi2/nDoF < 25, #Layers >= 7, #Hits >= 12 + std::vector<const Trk::Track*> goodTracks {}; for (const Trk::Track* track : *trackCollection) { if (track == nullptr) continue; @@ -951,6 +971,13 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const m_y_atMaxRadius.push_back(positionAtMaxRadius.y()); m_z_atMaxRadius.push_back(positionAtMaxRadius.z()); + if ((m_Chi2.back() / m_DoF.back() < m_maxChi2NDOF) && + (m_nLayers.back() >= m_minLayers) && + (m_DoF.back() + 5 >= m_minHits) && + (m_r_atMaxRadius.back() < 95)) { + goodTracks.push_back(track); + } + if (isMC) { // if simulation, store track truth info as well auto [truthParticle, hitCount] = m_trackTruthMatchingTool->getTruthParticle(track); if (truthParticle != nullptr) { @@ -958,6 +985,13 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const truthParticleCount[truthParticle->barcode()] += 1; m_t_pdg.push_back(truthParticle->pdgId()); m_t_barcode.push_back(truthParticle->barcode()); + if (truthParticle->nParents() > 0){ + m_t_pdg_parent.push_back(truthParticle->parent(0)->pdgId()); + m_t_barcode_parent.push_back(truthParticle->parent(0)->barcode()); + } else { + m_t_pdg_parent.push_back(0); + m_t_barcode_parent.push_back(-1); + } // the track fit eats up 5 degrees of freedom, thus the number of hits on track is m_DoF + 5 m_t_truthHitRatio.push_back(hitCount / (m_DoF.back() + 5)); m_isFiducial.push_back(m_fiducialParticleTool->isFiducial(truthParticle->barcode())); @@ -1152,6 +1186,41 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const m_longTracks++; } + if (goodTracks.size() >= 2) { + // sort tracks my momentum and reconstruct vertex + std::sort(goodTracks.begin(), goodTracks.end(), [](const Trk::Track *lhs, const Trk::Track *rhs){ + return lhs->trackParameters()->front()->momentum().z() < rhs->trackParameters()->front()->momentum().z(); + }); + std::vector<const Trk::Track*> lowMomentumTracks {goodTracks[0], goodTracks[1]}; + + std::optional<FaserTracking::POCA> vertex = m_vertexingTool->getVertex(lowMomentumTracks); + if (vertex) { + Eigen::Vector3d position = vertex->position; + m_vertex_x = position.x(); + m_vertex_y = position.y(); + m_vertex_z = position.z(); + m_vertex_chi2 = vertex->chi2; + + // get track parameters of two tracks with lowest momentum at vertex + auto vertexTrackParameters0 = m_vertexingTool->extrapolateTrack(goodTracks[0], m_vertex_z); + if (vertexTrackParameters0 != nullptr) { + // convert momentum from GeV to MeV + m_vertex_px0 = vertexTrackParameters0->momentum().x() * 1e3; + m_vertex_py0 = vertexTrackParameters0->momentum().y() * 1e3; + m_vertex_pz0 = vertexTrackParameters0->momentum().z() * 1e3; + m_vertex_p0 = std::sqrt(m_vertex_px0*m_vertex_px0 + m_vertex_py0*m_vertex_py0 + m_vertex_pz0*m_vertex_pz0); + } + auto vertexTrackParameters1 = m_vertexingTool->extrapolateTrack(goodTracks[1], m_vertex_z); + if (vertexTrackParameters1 != nullptr) { + // convert momentum from GeV to MeV + m_vertex_px1 = vertexTrackParameters1->momentum().x() * 1e3; + m_vertex_py1 = vertexTrackParameters1->momentum().y() * 1e3; + m_vertex_pz1 = vertexTrackParameters1->momentum().z() * 1e3; + m_vertex_p1 = std::sqrt(m_vertex_px1*m_vertex_px1 + m_vertex_py1*m_vertex_py1 + m_vertex_pz1*m_vertex_pz1); + } + } + } + if (!isMC) { if (m_doTrackFilter) { // filter events: colliding bunches have at least one long track, non-colliding bunches have at least one long track or one calo module with raw_peak > 3 mV if (m_longTracks == 0 && abs(m_distanceToCollidingBCID) <= 1) { @@ -1400,6 +1469,8 @@ NtupleDumperAlg::clearTree() const m_t_pdg.clear(); m_t_barcode.clear(); + m_t_pdg_parent.clear(); + m_t_barcode_parent.clear(); m_t_truthHitRatio.clear(); m_t_prodVtx_x.clear(); m_t_prodVtx_y.clear(); @@ -1466,11 +1537,25 @@ NtupleDumperAlg::clearTree() const m_truthd1_y.clear(); m_truthd1_z.clear(); + m_vertex_x = NaN; + m_vertex_y = NaN; + m_vertex_z = NaN; + m_vertex_chi2 = NaN; + m_vertex_px0 = NaN; + m_vertex_py0 = NaN; + m_vertex_pz0 = NaN; + m_vertex_p0 = NaN; + m_vertex_px1 = NaN; + m_vertex_py1 = NaN; + m_vertex_pz1 = NaN; + m_vertex_p1 = NaN; } void NtupleDumperAlg::clearTrackTruth() const { m_t_pdg.push_back(0); m_t_barcode.push_back(-1); + m_t_pdg_parent.push_back(0); + m_t_barcode_parent.push_back(-1); m_t_truthHitRatio.push_back(0); m_t_prodVtx_x.push_back(999999); m_t_prodVtx_y.push_back(999999); diff --git a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h index 67f3bacf8c8f4a6ef134741637d093fdea60c24b..d94379ca4cdc62741cea0a0edd06089382718436 100644 --- a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h +++ b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h @@ -23,6 +23,7 @@ #include "TrackerSimEvent/FaserSiHitCollection.h" #include "xAODEventInfo/EventInfo.h" #include "StoreGate/ReadDecorHandle.h" +#include "FaserActsVertexing/IVertexingTool.h" #include <vector> #include <nlohmann/json.hpp> @@ -93,6 +94,7 @@ private: ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool {this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"}; ToolHandle<ITrackTruthMatchingTool> m_trackTruthMatchingTool {this, "TrackTruthMatchingTool", "TrackTruthMatchingTool"}; ToolHandle<IFiducialParticleTool> m_fiducialParticleTool {this, "FiducialParticleTool", "FiducialParticleTool"}; + ToolHandle<FaserTracking::IVertexingTool> m_vertexingTool { this, "VertexingTool", "FaserTracking::PointOfClosestApproachSearchTool"}; const TrackerDD::SCT_DetectorManager* m_detMgr {nullptr}; @@ -124,6 +126,11 @@ private: BooleanProperty m_applyGoodRunsList {this, "ApplyGoodRunsList", false, "Only write out events passing GRL (data only)"}; StringProperty m_goodRunsList {this, "GoodRunsList", "", "GoodRunsList in json format"}; + // track quality cuts + UnsignedIntegerProperty m_minLayers{this, "minLayers", 7, "Miminimum number of layers of a track."}; + UnsignedIntegerProperty m_minHits{this, "minHits", 12, "Miminimum number of hits of a track."}; + DoubleProperty m_maxChi2NDOF{this, "maxChi2NDOF", 25, "Maximum chi2 per degree of freedom."}; + // json object to hold data read from GRL file (or empty if not) nlohmann::json m_grl; @@ -274,6 +281,8 @@ private: mutable std::vector<int> m_t_pdg; // pdg code of the truth matched particle mutable std::vector<int> m_t_barcode; // barcode of the truth matched particle + mutable std::vector<int> m_t_pdg_parent; // pdg code of the parent of the truth matched particle + mutable std::vector<int> m_t_barcode_parent; // barcode of the parent of the truth matched particle mutable std::vector<double> m_t_truthHitRatio; // ratio of hits on track matched to the truth particle over all hits on track mutable std::vector<double> m_t_prodVtx_x; // x component of the production vertex in mm mutable std::vector<double> m_t_prodVtx_y; // y component of the production vertex in mm @@ -355,6 +364,19 @@ private: mutable int m_eventsPassed = 0; mutable int m_eventsFailedGRL = 0; + + mutable double m_vertex_x; // components of reconstructed vertex in mm + mutable double m_vertex_y; + mutable double m_vertex_z; + mutable double m_vertex_chi2; // chi2 value of vertex fit + mutable double m_vertex_px0; // components of lowest momentum track extrapolated to vertex in MeV + mutable double m_vertex_py0; + mutable double m_vertex_pz0; + mutable double m_vertex_p0; + mutable double m_vertex_px1; // components of second lowest momentum track extrapolated to vertex in MeV + mutable double m_vertex_py1; + mutable double m_vertex_pz1; + mutable double m_vertex_p1; }; inline const ServiceHandle <ITHistSvc> &NtupleDumperAlg::histSvc() const { diff --git a/Tracking/Acts/FaserActsGeometry/src/FaserActsExtrapolationTool.cxx b/Tracking/Acts/FaserActsGeometry/src/FaserActsExtrapolationTool.cxx index d31fe8d3c11e804e115aef2cc6dfc08a2354b9e7..14d2699822166993a61b0de47a74c1fc5b139f65 100644 --- a/Tracking/Acts/FaserActsGeometry/src/FaserActsExtrapolationTool.cxx +++ b/Tracking/Acts/FaserActsGeometry/src/FaserActsExtrapolationTool.cxx @@ -71,7 +71,7 @@ FaserActsExtrapolationTool::initialize() ATH_MSG_INFO("Initializing ACTS extrapolation"); - m_logger = Acts::getDefaultLogger("ExtrapolationTool", Acts::Logging::INFO); + m_logger = Acts::getDefaultLogger("ExtrapolationTool", Acts::Logging::ERROR); // m_logger = makeActsAthenaLogger(this, "Prop", "FaserActsExtrapTool"); ATH_CHECK( m_trackingGeometryTool.retrieve() ); diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsVertexing/CMakeLists.txt b/Tracking/Acts/FaserActsKalmanFilter/FaserActsVertexing/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..61908a643b1d9f03eaa3b4ebf9c6eb2d34786a6a --- /dev/null +++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsVertexing/CMakeLists.txt @@ -0,0 +1,44 @@ +atlas_subdir(FaserActsVertexing) + +find_package( Eigen ) +find_package( Acts COMPONENTS Core ) + +atlas_add_library( FaserActsmanVertexingLib + PUBLIC_HEADERS FaserActsVertexing + INCLUDE_DIRS ${EIGEN_INCLUDE_DIRS} + LINK_LIBRARIES ${EIGEN_LIBRARIES} + ActsCore + AthenaBaseComps + FaserActsGeometryLib + FaserActsKalmanFilterLib + MagFieldConditions + StoreGateLib + TrackerPrepRawData + TrackerRIO_OnTrack + TrackerSimEvent + TrackerSpacePoint + TrkTrack + xAODTruth +) + +atlas_add_component( + FaserActsVertexing + FaserActsVertexing/IVertexingTool.h + src/PointOfClosestApproachSearchTool.h + src/PointOfClosestApproachSearchTool.cxx + src/components/FaserActsVertexing_entries.cxx + INCLUDE_DIRS ${EIGEN_INCLUDE_DIRS} + LINK_LIBRARIES ${EIGEN_LIBRARIES} + ActsCore + AthenaBaseComps + FaserActsGeometryLib + FaserActsKalmanFilterLib + MagFieldConditions + StoreGateLib + TrackerPrepRawData + TrackerRIO_OnTrack + TrackerSimEvent + TrackerSpacePoint + TrkTrack + xAODTruth +) diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsVertexing/FaserActsVertexing/IVertexingTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsVertexing/FaserActsVertexing/IVertexingTool.h new file mode 100644 index 0000000000000000000000000000000000000000..3c6f93714b6cf07f2428a80c157318399d7b632e --- /dev/null +++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsVertexing/FaserActsVertexing/IVertexingTool.h @@ -0,0 +1,37 @@ +#ifndef FASERACTSVERTEXING_IVERTEXINGTOOL_H +#define FASERACTSVERTEXING_IVERTEXINGTOOL_H + +#include "Acts/EventData/TrackParameters.hpp" +#include "GaudiKernel/IAlgTool.h" +#include "GaudiKernel/IInterface.h" +#include "TrkTrack/Track.h" +#include <vector> + +namespace FaserTracking { + +// Point of closest approach between n tracks. Contains the three-dimensional +// reconstructed position, the chi2 value of the vertex fit and the tracks that +// originate from this vertex. +struct POCA { + POCA(Eigen::Vector3d vertex, double chi2, std::vector<const Trk::Track *> tracks) + : position(vertex), chi2(chi2), tracks(tracks) {} + Eigen::Vector3d position; + double chi2; + std::vector<const Trk::Track *> tracks; +}; + +class IVertexingTool : virtual public IAlgTool { +public: + DeclareInterfaceID(IVertexingTool, 1, 0); + + // Run vertex fit and return the fitted POCA. + virtual std::optional<POCA> getVertex(const std::vector<const Trk::Track *> &tracks) const = 0; + + // Get bound track parameters at given z position. + virtual std::unique_ptr<const Acts::BoundTrackParameters> + extrapolateTrack(const Trk::Track *track, double targetPosition) const = 0; +}; + +} // namespace FaserTracking + +#endif // FASERACTSVERTEXING_IVERTEXINGTOOL_H diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsVertexing/src/PointOfClosestApproachSearchTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/FaserActsVertexing/src/PointOfClosestApproachSearchTool.cxx new file mode 100644 index 0000000000000000000000000000000000000000..ea423d703372dbfcc954dc2089b8169282cdc634 --- /dev/null +++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsVertexing/src/PointOfClosestApproachSearchTool.cxx @@ -0,0 +1,212 @@ +#include "PointOfClosestApproachSearchTool.h" +#include "TrkParameters/TrackParameters.h" +#include <climits> + +namespace FaserTracking { + +PointOfClosestApproachSearchTool::PointOfClosestApproachSearchTool(const std::string &type, + const std::string &name, + const IInterface *parent) + : base_class(type, name, parent) {} + +StatusCode PointOfClosestApproachSearchTool::initialize() { + ATH_CHECK(m_trackingGeometryTool.retrieve()); + ATH_CHECK(m_extrapolationTool.retrieve()); + return StatusCode::SUCCESS; +} + +StatusCode PointOfClosestApproachSearchTool::finalize() { return StatusCode::SUCCESS; } + +std::optional<POCA> +PointOfClosestApproachSearchTool::getVertex(const std::vector<const Trk::Track *> &tracks) const { + + if (tracks.size() < 2) { + ATH_MSG_DEBUG("Found " << tracks.size() << " tracks, but require at least 2 tracks."); + return {}; + } else if (tracks.size() > 3) { + ATH_MSG_DEBUG("Found " << tracks.size() << " tracks, but require at most 3 tracks."); + return {}; + } + + clear(); + + m_ctx = Gaudi::Hive::currentContext(); + m_gctx = m_trackingGeometryTool->getNominalGeometryContext().context(); + + // convert to Acts::BoundTrackParameters + std::vector<Acts::BoundTrackParameters> trackParameters{}; + for (const Trk::Track *track : tracks) { + trackParameters.push_back(getParametersFromTrack(track->trackParameters()->front())); + } + + // extrapolate to nSteps z positions betweeen zMin and zMax and calculate center of tracks and + // chi2 for each position. + size_t nSteps = (m_zMax - m_zMin) / m_stepSize + 1; + m_allVertexCandidates.reserve(nSteps); + for (int z = m_zMin; z <= m_zMax; z += m_stepSize) { + auto vertexCandidate = getVertexCandidate(trackParameters, z); + if (vertexCandidate) { + m_allVertexCandidates.push_back(*vertexCandidate); + } + } + + // find vertex candidates with minium (local) chi2 value + for (size_t i = 1; i < nSteps - 1; ++i) { + if ((m_allVertexCandidates[i - 1].chi2 > m_allVertexCandidates[i].chi2) and + (m_allVertexCandidates[i + 1].chi2 > m_allVertexCandidates[i].chi2)) { + m_goodVertexCandidates.push_back(m_allVertexCandidates[i]); + } + } + + // Check if there are one or two local minima and calculate mean in the case of two minima. + if (m_goodVertexCandidates.size() == 0) { + ATH_MSG_DEBUG("Cannot find minimum. Extrapolation failed. This is likely an error."); + return {}; + } else if (m_goodVertexCandidates.size() == 1) { + const VertexCandidate &vx = m_goodVertexCandidates.front(); + return POCA(vx.position, vx.chi2, tracks); + } else if (m_goodVertexCandidates.size() == 2) { + const VertexCandidate &vx0 = m_goodVertexCandidates.at(0); + const VertexCandidate &vx1 = m_goodVertexCandidates.at(1); + return POCA(0.5 * (vx0.position + vx1.position), 0.5 * (vx0.chi2 + vx1.chi2), tracks); + } else { + ATH_MSG_DEBUG("Cannot find global minimum. Expect a maximum of two local chi2 minima but found " + << m_goodVertexCandidates.size() << " minima."); + return {}; + } +} + +// Calculate center of tracks +Eigen::Vector2d PointOfClosestApproachSearchTool::getCenter( + const std::vector<PointOfClosestApproachSearchTool::TrackPosition> &trackPositions) const { + Eigen::Vector2d center{0, 0}; + Eigen::Vector2d sumInverse{0, 0}; + for (const TrackPosition &value : trackPositions) { + center(0) += value.inv(0, 0) * value.pos(0); + sumInverse(0) += value.inv(0, 0); + center(1) += value.inv(1, 1) * value.pos(1); + sumInverse(1) += value.inv(1, 1); + } + center(0) /= sumInverse(0); + center(1) /= sumInverse(1); + return center; +} + +// Extrapolate track to given z positon. +std::unique_ptr<const Acts::BoundTrackParameters> +PointOfClosestApproachSearchTool::extrapolateTrack(const Trk::Track *track, + double targetPosition) const { + Acts::BoundTrackParameters startParameters = + getParametersFromTrack(track->trackParameters()->front()); + auto targetSurface = Acts::Surface::makeShared<Acts::PlaneSurface>( + Acts::Vector3(0, 0, targetPosition), Acts::Vector3(0, 0, 1)); + Acts::NavigationDirection navigationDirection = + targetPosition > startParameters.referenceSurface().center(m_gctx).z() ? Acts::forward + : Acts::backward; + return m_extrapolationTool->propagate(m_ctx, startParameters, *targetSurface, + navigationDirection); +} + +std::optional<PointOfClosestApproachSearchTool::TrackPosition> +PointOfClosestApproachSearchTool::extrapolateTrackParameters( + const Acts::BoundTrackParameters &startParameters, double z) const { + auto targetSurface = + Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, z), Acts::Vector3(0, 0, 1)); + Acts::NavigationDirection navigationDirection = + z > startParameters.referenceSurface().center(m_gctx).z() ? Acts::forward : Acts::backward; + std::unique_ptr<const Acts::BoundTrackParameters> targetParameters = + m_extrapolationTool->propagate(m_ctx, startParameters, *targetSurface, navigationDirection); + if (targetParameters != nullptr && targetParameters->covariance().has_value()) { + // extrapolate covariance matrix? + // return TrackPosition{targetParameters->position(m_gctx), + // startParameters.covariance().value().topLeftCorner(2, 2)}; + return TrackPosition(targetParameters->position(m_gctx), + targetParameters->covariance().value().topLeftCorner(2, 2)); + } else { + ATH_MSG_DEBUG("Extrapolation failed."); + return std::nullopt; + } +} + +std::optional<PointOfClosestApproachSearchTool::VertexCandidate> +PointOfClosestApproachSearchTool::getVertexCandidate( + std::vector<Acts::BoundTrackParameters> &trackParameters, double z) const { + // extrapolate tracks to given z position + std::vector<TrackPosition> trackPositions{}; + for (const Acts::BoundTrackParameters &startParameters : trackParameters) { + auto tp = extrapolateTrackParameters(startParameters, z); + if (tp) { + trackPositions.push_back(*tp); + } + } + if (trackPositions.size() < 2) { + ATH_MSG_DEBUG( + "Extrapolation failed for too many tracks. Require atleast 2 extrapolated positions."); + return std::nullopt; + } + + // calculate center weighted by inverse covariance matrix + Eigen::Vector2d trackCenter = getCenter(trackPositions); + + // either use weighted x and y position or only y position to calculate chi2 value + double vertexChi2 = chi2Y(trackPositions, trackCenter); + + return PointOfClosestApproachSearchTool::VertexCandidate(z, vertexChi2, trackCenter); +} + +double PointOfClosestApproachSearchTool::chi2(const std::vector<TrackPosition> &trackPositions, + const Eigen::Vector2d &trackCenter) const { + double chi2 = 0; + for (const TrackPosition &value : trackPositions) { + Eigen::Vector2d delta = trackCenter - value.pos; + chi2 += delta.dot(value.inv.diagonal().cwiseProduct(delta)); + } + return chi2; +} + +double PointOfClosestApproachSearchTool::chi2Y(const std::vector<TrackPosition> &trackPositions, + const Eigen::Vector2d &trackCenter) const { + double chi2 = 0; + for (const TrackPosition &value : trackPositions) { + Eigen::Vector2d delta = trackCenter - value.pos; + chi2 += delta.y() * value.inv(1, 1) * delta.y(); + } + return chi2; +} + +Acts::BoundTrackParameters PointOfClosestApproachSearchTool::getParametersFromTrack( + const Trk::TrackParameters *trackParameters) const { + using namespace Acts::UnitLiterals; + + Acts::GeometryContext geometryContext = + m_trackingGeometryTool->getNominalGeometryContext().context(); + Acts::AngleAxis3 rotZ(M_PI / 2, Acts::Vector3(0., 0., 1.)); + Acts::Translation3 translation{0., 0., trackParameters->associatedSurface().center().z()}; + auto transform = Acts::Transform3(translation * rotZ); + auto surface = Acts::Surface::makeShared<Acts::PlaneSurface>(transform); + + auto bound = Acts::detail::transformFreeToBoundParameters( + trackParameters->position(), 0, trackParameters->momentum(), + trackParameters->charge() / trackParameters->momentum().mag() / 1_MeV, *surface, + geometryContext); + if (!bound.ok()) { + ATH_MSG_WARNING("FreeToBound parameter transformation failed"); + } + // convert covariance matrix to GeV + Acts::BoundSymMatrix cov = Acts::BoundSymMatrix::Identity(); + cov.topLeftCorner(5, 5) = *trackParameters->covariance(); + for (int i = 0; i < cov.rows(); i++) { + cov(i, 4) = cov(i, 4) / 1_MeV; + } + for (int i = 0; i < cov.cols(); i++) { + cov(4, i) = cov(4, i) / 1_MeV; + } + return Acts::BoundTrackParameters{surface, bound.value(), trackParameters->charge(), cov}; +} + +void PointOfClosestApproachSearchTool::clear() const { + m_allVertexCandidates.clear(); + m_goodVertexCandidates.clear(); +} + +} // namespace FaserTracking diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsVertexing/src/PointOfClosestApproachSearchTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsVertexing/src/PointOfClosestApproachSearchTool.h new file mode 100644 index 0000000000000000000000000000000000000000..9ed794948483ee01fcfb13203971ba7a897657da --- /dev/null +++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsVertexing/src/PointOfClosestApproachSearchTool.h @@ -0,0 +1,84 @@ +#ifndef FASERACTSVERTEXING_POINTOFCLOSESTAPPROACHSEARCHTOOL_H +#define FASERACTSVERTEXING_POINTOFCLOSESTAPPROACHSEARCHTOOL_H + +#include "TrackerPrepRawData/FaserSCT_ClusterContainer.h" +#include "Acts/EventData/TrackParameters.hpp" +#include "AthenaBaseComps/AthAlgTool.h" +#include "FaserActsGeometryInterfaces/IFaserActsExtrapolationTool.h" +#include "FaserActsGeometryInterfaces/IFaserActsTrackingGeometryTool.h" +#include "FaserActsVertexing/IVertexingTool.h" +#include "TrkTrack/Track.h" + +namespace FaserTracking { + +class PointOfClosestApproachSearchTool : public extends<AthAlgTool, IVertexingTool> { +public: + PointOfClosestApproachSearchTool(const std::string &type, const std::string &name, + const IInterface *parent); + virtual ~PointOfClosestApproachSearchTool() = default; + virtual StatusCode initialize() override; + virtual StatusCode finalize() override; + + std::optional<POCA> getVertex(const std::vector<const Trk::Track *> &tracks) const override; + + std::unique_ptr<const Acts::BoundTrackParameters> + extrapolateTrack(const Trk::Track *track, double targetPosition) const override; + +private: + struct VertexCandidate { + VertexCandidate(double z, double chi2, const Eigen::Vector2d ¢er) : chi2(chi2) { + position = Eigen::Vector3d(center.x(), center.y(), z); + } + double chi2; + Eigen::Vector3d position; + }; + + struct TrackPosition { + TrackPosition(const Acts::Vector3 &position, const Acts::BoundSymMatrix &covariance) { + pos << position.x(), position.y(); + cov << covariance(1, 1), 0, 0, covariance(0, 0); + inv = cov.inverse(); + } + Eigen::Vector2d pos; + Eigen::Matrix2d cov; + Eigen::Matrix2d inv; + }; + + ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool{ + this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"}; + ToolHandle<IFaserActsExtrapolationTool> m_extrapolationTool{ + this, "ExtrapolationTool", "FaserActsExtrapolationTool"}; + DoubleProperty m_stepSize{this, "StepSize", 50}; + DoubleProperty m_zMin{this, "ZMin", -2000}; + DoubleProperty m_zMax{this, "ZMax", 500}; + StringProperty m_debugLevel{this, "DebugLevel", "INFO"}; + + void clear() const; + + std::optional<TrackPosition> + extrapolateTrackParameters(const Acts::BoundTrackParameters &startParameters, double z) const; + + Eigen::Vector2d getCenter(const std::vector<TrackPosition> &trackPositions) const; + + Acts::BoundTrackParameters + getParametersFromTrack(const Trk::TrackParameters *trackParameters) const; + + double chi2(const std::vector<TrackPosition> &trackPositions, + const Eigen::Vector2d &trackCenter) const; + + double chi2Y(const std::vector<TrackPosition> &trackPositions, + const Eigen::Vector2d &trackCenter) const; + + std::optional<VertexCandidate> + getVertexCandidate(std::vector<Acts::BoundTrackParameters> &trackParameters, double z) const; + + mutable EventContext m_ctx; + mutable Acts::GeometryContext m_gctx; + + mutable std::vector<VertexCandidate> m_allVertexCandidates; + mutable std::vector<VertexCandidate> m_goodVertexCandidates; +}; + +} // namespace FaserTracking + +#endif /* FASERACTSVERTEXING_POINTOFCLOSESTAPPROACHSEARCHTOOL_H */ diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsVertexing/src/components/FaserActsVertexing_entries.cxx b/Tracking/Acts/FaserActsKalmanFilter/FaserActsVertexing/src/components/FaserActsVertexing_entries.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4f27a5a3d95ba97b48d36ac0b43c634c1d99d9ae --- /dev/null +++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsVertexing/src/components/FaserActsVertexing_entries.cxx @@ -0,0 +1,3 @@ +#include "../PointOfClosestApproachSearchTool.h" + +DECLARE_COMPONENT(FaserTracking::PointOfClosestApproachSearchTool) \ No newline at end of file