Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -247,3 +247,6 @@ test/k4FWCoreTest/**/*.root
test/inputFiles/*.slcio
test/gaudi_opts/testConverterConstants.py

# TrackingValidation test outputs
TrackingPerformance/test/*.root
TrackingPerformance/test/*.log
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@ function(set_test_env _testname)
endfunction()

add_subdirectory(RecoMCTruthLinkers)
add_subdirectory(TrackingPerformance)

include(cmake/CreateProjectConfig.cmake)
103 changes: 103 additions & 0 deletions TrackingPerformance/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#[[
Copyright (c) 2020-2024 Key4hep-Project.

This file is part of Key4hep.
See https://key4hep.github.io/key4hep-doc/ for further info.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]

set(PackageName TrackingPerformance)

project(${PackageName})

include(ExternalData)
list(APPEND ExternalData_URL_TEMPLATES
"https://key4hep.web.cern.ch:443/testFiles/k4RecTracker/%(hash)"
)

file(GLOB sources
${PROJECT_SOURCE_DIR}/src/*.cc
${PROJECT_SOURCE_DIR}/src/*.cpp
${PROJECT_SOURCE_DIR}/src/components/*.cpp
)

file(GLOB headers
${PROJECT_SOURCE_DIR}/include/*.h
)

find_package(ROOT REQUIRED COMPONENTS Core RIO Tree MathCore MathMore Graf Graf3d Hist)

gaudi_add_module(${PackageName}
SOURCES ${sources}
LINK
Gaudi::GaudiKernel
EDM4HEP::edm4hep
k4FWCore::k4FWCore
ROOT::MathCore
ROOT::MathMore
ROOT::Physics
ROOT::Graf
ROOT::Graf3d
ROOT::Hist
)

target_include_directories(${PackageName} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
${MarlinUtil_INCLUDE_DIRS}
${DELPHES_INCLUDE_DIRS}
)

set_target_properties(${PackageName} PROPERTIES
PUBLIC_HEADER "${headers}"
)

file(GLOB test_python_scripts
${PROJECT_SOURCE_DIR}/test/*.py
)

set(test_shell_scripts
${PROJECT_SOURCE_DIR}/test/testTrackingValidation.sh
)

set(test_scripts
${test_python_scripts}
${test_shell_scripts}
)

file(COPY ${test_scripts} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/test)

install(TARGETS ${PackageName}
EXPORT ${CMAKE_PROJECT_NAME}Targets
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT bin
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shlib
PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${CMAKE_PROJECT_NAME}" COMPONENT dev
)
message(STATUS "CMAKE_SOURCE_DIR = ${CMAKE_SOURCE_DIR}")
message(STATUS "CMAKE_CURRENT_SOURCE_DIR = ${CMAKE_CURRENT_SOURCE_DIR}")
message(STATUS "PROJECT_SOURCE_DIR = ${PROJECT_SOURCE_DIR}")
install(FILES ${test_scripts} DESTINATION test)

SET(test_name "testTrackingValidation")

ExternalData_Add_Test(testTrackingValidation
NAME ${test_name}
COMMAND sh +x TrackingPerformance/test/testTrackingValidation.sh
DATA{${CMAKE_SOURCE_DIR}/TrackingPerformance/test/inputFiles/SimpleGatrIDEAv3o1.onnx}
)

set_test_env(${test_name})
set_tests_properties(${test_name} PROPERTIES WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
ExternalData_Add_Target(${test_name})

178 changes: 178 additions & 0 deletions TrackingPerformance/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# TrackingValidation

## Overview

`TrackingValidation` is a validation algorithm for studying the performance (efficiency, purity, residuals, resolutions) of track finding and track fitting in the tracking reconstruction.
It is designed to compare reconstructed and fitted tracks with Monte Carlo truth information and, when enabled, with tracks obtained from perfect tracking, i.e. tracks fitted using the correct simHits from the particle truth information. The algorithm writes validation information to a ROOT output file containing TTrees and summary plots that can be used later for performance studies and plotting.

Typical use cases include:
- validation of track-finder performance,
- validation of fitted-track parameters against MC truth,
- comparison between standard reconstructed tracks and perfectly associated reference tracks.

---

## Inputs

`TrackingValidation` expects EDM4hep event content in which the relevant collections have already been produced by the preceding steps of the reconstruction chain.

### Input collection types

`TrackingValidation` consumes the following input collections:

- **MC particle collection**
Type: `edm4hep::MCParticleCollection`
Used as the truth reference for particle-level validation.

- **Planar digi-to-sim link collections**
Type: `std::vector<const edm4hep::TrackerHitSimTrackerHitLinkCollection*>`
Used to connect reconstructed planar hits to the originating simulated particles.

- **Drift-chamber digi-to-sim link collections**
Type: `std::vector<const edm4hep::TrackerHitSimTrackerHitLinkCollection*>`
Used to connect reconstructed drift-chamber hits to the originating simulated particles.

- **Finder track collection**
Type: `edm4hep::TrackCollection`
Collection of tracks produced by the track-finding stage.

- **Fitted track collection**
Type: `edm4hep::TrackCollection`
Collection of tracks produced by the standard fitting stage.

- **Perfect fitted-track collections (optional)**
Type: `std::vector<const edm4hep::TrackCollection*>`
Optional reference collections produced from perfect truth-based associations, used when perfect-fit validation is enabled.

---

## Outputs

The algorithm writes a ROOT file specified by `OutputFile`.

The file contains validation TTrees for finder-level and fitter-level studies, together with summary performance plots produced in `finalize()`. The fitter validation trees store residuals of the reconstructed track parameters with respect to the chosen reference.

### Output content by mode

The exact content filled in the output depends on the validation mode selected through `Mode`:

- **`Mode = 0` (full-pipeline mode)**
Both finder-level and fitter-level validation are performed.
The output includes the association trees and the fitter residual trees.

- **`Mode = 1` (finder-only mode)**
Only the finder-level validation is performed.
The finder and perfect-association trees are filled, while the fitter trees are booked in the file but are not filled.

- **`Mode = 2` (fitter-only mode)**
Only the fitter-level validation is performed.
The fitter trees are filled, while the finder and perfect-association trees are booked in the file but are not filled.

### Effect of `DoPerfectFit`

The flag `DoPerfectFit` controls the handling of the `fitter_vs_perfect` output:

- if **`DoPerfectFit = true`** and perfect fitted-track collections are provided, the fitter-to-perfect comparison is filled;
- if **`DoPerfectFit = false`**, the `fitter_vs_perfect` tree is still created but its per-event content remains empty;
- if **`DoPerfectFit = true`** but no perfect fitted-track collection is provided, the tree is still written and a warning is issued.

### Summary plots

In `finalize()`, the algorithm also writes summary plots to the same ROOT file, including:

- tracking efficiency vs momentum,
- `d0` resolution vs momentum,
- momentum resolution vs momentum,
- transverse-momentum resolution vs momentum.

---

## Finder validation: efficiency and purity

To evaluate finder performance, each reconstructed track is matched to the truth particle with which it shares the largest number of hits.

For each particle-track pair, the algorithm stores two standard hit-based quantities:

- **track hit purity**: the fraction of hits on the reconstructed track that originate from the matched truth particle;
- **track hit efficiency**: the fraction of the truth-particle hits that are recovered in the reconstructed track.

The summary **tracking efficiency** can then be defined in more than one way.

- **`FinderEfficiencyDefinition = 1`**
A truth particle is counted as reconstructed if it is associated to at least one finder track with
`purity >= FinderPurityThreshold`.
In the default configuration, `FinderPurityThreshold = 0.75`, following the CMS association convention in which a reconstructed track is associated to a simulated particle if more than 75% of its hits originate from that particle. The tracking efficiency is then defined as the fraction of simulated tracks associated to at least one reconstructed track. :contentReference[oaicite:0]{index=0}

- **`FinderEfficiencyDefinition = 2`**
A truth particle is counted as reconstructed if it is associated to at least one finder track with
`purity >= 0.5` **and** `efficiency >= 0.5`.
This corresponds to the stricter two-ratio variant, where both the purity of the reconstructed track and the fraction of recovered truth hits must exceed 50%.

In the current implementation, the denominator of the efficiency plot includes generator-level particles with status 1 and at least one truth-linked hit.
For more details on the CMS association convention and the related definitions of tracking efficiency, fake rate, and duplicate rate, see the CMS performance note [*Performance of the track selection DNN in Run 3*](https://cds.cern.ch/record/2854696/files/DP2023_009.pdf).




---

## How to run

`TrackingValidation` is tested through a small end-to-end workflow driven by `ctest`. The test starts from a simulated EDM4hep file, runs the reconstruction and validation steering, and writes the final validation ROOT output.

In the current setup:

- the simulation step is performed with `ddsim` in the shell test,
- the reconstruction and validation steps are controlled by `runTrackingValidation.py`,
- the full test is launched through `ctest`.

Run the test from the build directory:

```bash
cd k4DetectorPerformance/build
ctest -V -R testTrackingValidation
```

The validation output is written to:

```text
k4DetectorPerformance/TrackingPerformance/test/validation_output_test.root
```
### Test configuration and steering options

The current test runs the full reconstruction and validation chain with the following settings:

- `TRACKINGPERF_RUN_SIM = 1`
simulation step in the shell test (`0` = skip simulation and use an existing EDM4hep input file via `TRACKINGPERF_INPUT_FILE_OVERRIDE`, `1` = run simulation with `ddsim`);

- `runDigi = 1`
digitization step (`0` = skip digitization, `1` = run digitization);

- `runFinder = 1`
track-finder step (`0` = skip track finder, `1` = run track finder);

- `runFitter = 1`
reconstructed-track fitter (`0` = skip reco fitter, `1` = run reco fitter);

- `runPerfectTracking = 1`
perfect-tracking and perfect-fitter chain (`0` = skip perfect tracking/perfect fitter, `1` = run them);

- `runValidation = 1`
validation step (`0` = skip validation, `1` = run validation);

- `useDCH = 1`
drift-chamber collections (`0` = disable DCH, `1` = use DCH);

- `mode = 0`
validation mode (`0` = full validation, `1` = finder-only validation, `2` = fitter-only validation);

- `doPerfectFit = 1`
fitter-versus-perfect comparison (`0` = disable fitter-vs-perfect filling, `1` = enable it);

- `finderEfficiencyDefinition = 1`
tracking-efficiency definition (`1` = purity-based definition, `2` = purity >= 0.5 and efficiency >= 0.5);

- `finderPurityThreshold = 0.75`
purity threshold used when `FinderEfficiencyDefinition = 1`.

These command-line flags are defined in `runTrackingValidation.py`, which allows the same steering file to be used either for the full chain or for reduced workflows in which some reconstruction steps are skipped and only the validation is run.
99 changes: 99 additions & 0 deletions TrackingPerformance/include/TrackingValidationHelpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2020-2024 Key4hep-Project.
*
* This file is part of Key4hep.
* See https://key4hep.github.io/key4hep-doc/ for further info.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef TRACKINGVALIDATIONHELPERS_H
#define TRACKINGVALIDATIONHELPERS_H

#include "edm4hep/MCParticle.h"
#include "edm4hep/Track.h"
#include "edm4hep/TrackState.h"
#include "podio/ObjectID.h"

#include <cstdint>

namespace TrackingValidationHelpers {

/// Container for helix parameters in the fitter convention
struct HelixParams {
float D0 = 0.f;
float Z0 = 0.f;
float phi = 0.f;
float omega = 0.f;
float tanLambda = 0.f;
float p = 0.f;
float pT = 0.f;
};

/// Helper container for PCA position and tangent angle
struct PCAInfoHelper {
float pcaX = 0.f;
float pcaY = 0.f;
float pcaZ = 0.f;
float phi0 = 0.f;
bool ok = false;
};

/// Build a unique integer key from a podio object identifier
uint64_t oidKey(const podio::ObjectID& id);
Comment on lines +52 to +53
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

podio::ObjectID is hashable (since podio v01-03) and it should be possible to directly use it in e.g. std::unordered_map as a key type. There should be no need for constructing a uint64_t for this purpose.


/// Safe wrapper around std::atan2
float safeAtan2(float y, float x);

/// Wrap a phi difference into the interval [-pi, pi]
float wrapDeltaPhi(float a, float b);

/**
* @brief Compute the PCA position and tangent angle in mm using a GenFit-like convention.
*
* The function derives the point of closest approach to the reference point
* and the corresponding tangent direction from the input position, momentum,
* charge sign, and magnetic field.
*/

PCAInfoHelper PCAInfo_mm(float x, float y, float z,
float px, float py, float pz,
int chargeSign,
float refX, float refY,
float Bz);


/**
* @brief Build truth helix parameters in the same convention used by the fitter.
*
* The returned parameters are derived from the MC particle kinematics and vertex
* using the same reference-point and helix convention adopted for fitted tracks,
* so that residuals can be computed consistently.
*/

HelixParams truthFromMC_GenfitConvention(const edm4hep::MCParticle& mc,
float Bz,
float refX, float refY, float refZ);

/// Retrieve the track state stored at the interaction point
bool getAtIPState(const edm4hep::Track& trk, edm4hep::TrackState& out);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could return a std::optional instead of using an in-out parameter via reference. I have also opened key4hep/EDM4hep#493 which should make this obsolete.


/// Compute the transverse momentum from a track state
float ptFromState(const edm4hep::TrackState& st, float Bz);

/// Compute the total momentum from a track state
float momentumFromState(const edm4hep::TrackState& st, float Bz);

} // namespace TrackingValidationHelpers

#endif
Loading