From d1b3f78584a55396cd4241a6b538c858cb88aa06 Mon Sep 17 00:00:00 2001 From: Benjamin Huth Date: Thu, 19 Mar 2026 17:33:17 +0100 Subject: [PATCH 1/6] do it --- Examples/Scripts/Python/gnn_module_map_odd.py | 22 +++---- Python/Examples/python/reconstruction.py | 57 +++++++++++-------- Python/Examples/tests/root_file_hashes.txt | 6 +- Python/Examples/tests/test_examples.py | 8 ++- 4 files changed, 53 insertions(+), 40 deletions(-) diff --git a/Examples/Scripts/Python/gnn_module_map_odd.py b/Examples/Scripts/Python/gnn_module_map_odd.py index de45ce77c79..0681ce7d0f0 100644 --- a/Examples/Scripts/Python/gnn_module_map_odd.py +++ b/Examples/Scripts/Python/gnn_module_map_odd.py @@ -25,7 +25,7 @@ ParticleConfig, addFatras, addDigitization, - addGenParticleSelection, + addDigiParticleSelection, ParticleSelectorConfig, ) from acts.examples.reconstruction import addGnn, addSpacePointsMaking @@ -94,16 +94,6 @@ def runGnnModuleMap( rnd=rnd, ) - addGenParticleSelection( - s, - ParticleSelectorConfig( - rho=(0.0, 24 * u.mm), - absZ=(0.0, 1.0 * u.m), - eta=(-3.0, 3.0), - pt=(150 * u.MeV, None), - ), - ) - # FATRAS simulation addFatras( s, @@ -127,6 +117,16 @@ def runGnnModuleMap( logLevel=acts.logging.INFO, ) + addDigiParticleSelection( + s, + ParticleSelectorConfig( + pt=(1.0 * u.GeV, None), + eta=(-3.0, 3.0), + measurements=(7, None), + removeNeutral=True, + ), + ) + addSpacePointsMaking( s, trackingGeometry, diff --git a/Python/Examples/python/reconstruction.py b/Python/Examples/python/reconstruction.py index 6325175aa43..912de0b72d6 100644 --- a/Python/Examples/python/reconstruction.py +++ b/Python/Examples/python/reconstruction.py @@ -1817,6 +1817,7 @@ def addTrackWriters( writeStates: bool = False, writeFitterPerformance: bool = False, writeFinderPerformance: bool = False, + writeFinderNTuple: bool = False, logLevel: Optional[acts.logging.Level] = None, writeCovMat=False, ): @@ -1877,6 +1878,17 @@ def addTrackWriters( ) s.addWriter(trackFinderPerfWriter) + if writeFinderNTuple: + nTupleWriter = RootTrackFinderNTupleWriter( + level=customLogLevel(), + inputTracks="tracks", + inputParticles="particles_selected", + inputParticleMeasurementsMap="particle_measurements_map", + inputTrackParticleMatching="track_particle_matching", + filePath=str(Path(outputDirRoot) / f"ntuple_finding_{name}.root"), + ) + s.addWriter(nTupleWriter) + if outputDirCsv is not None: outputDirCsv = Path(outputDirCsv) if not outputDirCsv.exists(): @@ -1997,24 +2009,24 @@ def addGnn( s.addWhiteboardAlias("protoTracks", findingAlg.config.outputProtoTracks) # Convert proto tracks to tracks - s.addAlgorithm( - acts.examples.ProtoTracksToTracks( - level=customLogLevel(), - inputProtoTracks="protoTracks", - inputMeasurements="measurements", - outputTracks="tracks", - ) + convAlg = acts.examples.ProtoTracksToTracks( + level=customLogLevel(), + inputProtoTracks=findingAlg.config.outputProtoTracks, + inputMeasurements="measurements", + outputTracks="gnn-tracks", ) + s.addAlgorithm(convAlg) + s.addWhiteboardAlias("tracks", convAlg.config.outputTracks) # Truth matching matchAlg = acts.examples.TrackTruthMatcher( level=customLogLevel(), - inputTracks="tracks", - inputParticles="particles", + inputTracks=convAlg.config.outputTracks, + inputParticles="particles_selected", inputMeasurementParticlesMap="measurement_particles_map", outputTrackParticleMatching="gnn_track_particle_matching", outputParticleTrackMatching="gnn_particle_track_matching", - doubleMatching=True, + doubleMatching=False, ) s.addAlgorithm(matchAlg) s.addWhiteboardAlias( @@ -2024,21 +2036,16 @@ def addGnn( "particle_track_matching", matchAlg.config.outputParticleTrackMatching ) - # Optional performance writer - if outputDirRoot is not None: - assert ( - ACTS_EXAMPLES_ROOT_AVAILABLE - ), "ROOT output requested but ROOT is not available" - s.addWriter( - RootTrackFinderNTupleWriter( - level=customLogLevel(), - inputTracks="tracks", - inputParticles="particles", - inputParticleMeasurementsMap="particle_measurements_map", - inputTrackParticleMatching=matchAlg.config.outputTrackParticleMatching, - filePath=str(Path(outputDirRoot) / "performance_track_finding.root"), - ) - ) + addTrackWriters( + s, + name="gnn", + tracks=convAlg.config.outputTracks, + outputDirRoot=outputDirRoot, + outputDirCsv=None, + writeFinderPerformance=True, + writeSummary=False, + writeFinderNTuple=True, + ) return s diff --git a/Python/Examples/tests/root_file_hashes.txt b/Python/Examples/tests/root_file_hashes.txt index 6f245e9223b..6bdcd2ec6fc 100644 --- a/Python/Examples/tests/root_file_hashes.txt +++ b/Python/Examples/tests/root_file_hashes.txt @@ -76,8 +76,10 @@ test_root_clusters_writer[configKwConstructor]__clusters.root: e842df4fe04eefff3 test_root_clusters_writer[kwargsConstructor]__clusters.root: e842df4fe04eefff3df5f32cd1026e93286be62b8040dc700a2aff557c56dec8 test_gnn_metric_learning[gpu-onnx]__performance_track_finding.root: 767cc7bef38595b2d29a928d323abf1d11e514ed4d909249623d8ec8b679e5f2 test_gnn_metric_learning[gpu-torch]__performance_track_finding.root: 3f0fb36af55441994a154ea2a93978ba1930d4e87bf043f8ae9527e283bf1894 -test_gnn_module_map[gpu-torch]__performance_track_finding.root: db5c884283f5700cc2308bdf722e35b203ae018cbf10950fb71483c253685a3a -test_gnn_module_map[gpu-onnx]__performance_track_finding.root: 80e6651bb1249407d675ba2f7fd4f2af01c4fcb9c393e7f4e526212a0102b545 +test_gnn_module_map[gpu-torch]__performance_finding_gnn.root: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +test_gnn_module_map[gpu-torch]__ntuple_finding_gnn.root: 46e2577c4489d969c140a454c8ecaf7b22947324fc2df304e58a4929822c919f +test_gnn_module_map[gpu-onnx]__performance_finding_gnn.root: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +test_gnn_module_map[gpu-onnx]__ntuple_finding_gnn.root: 46e2577c4489d969c140a454c8ecaf7b22947324fc2df304e58a4929822c919f test_ML_Ambiguity_Solver__performance_finding_ambiML.root: c17fb877bb165e28db0a2b99881763093e7fc9a707c045feb6a6a6b68e0dd660 test_truth_tracking_kalman[generic-0.0]__trackstates_kf.root: 42a49abcef0277ca061350cc03c1ac9841e119055e2778662ec6a731e316ecd2 test_truth_tracking_kalman[generic-0.0]__tracksummary_kf.root: bde974e07d033f9bb8e078c180d288c996f69590d6b4516625e1ec0276f327a7 diff --git a/Python/Examples/tests/test_examples.py b/Python/Examples/tests/test_examples.py index 69506abf0b9..ca98584176d 100644 --- a/Python/Examples/tests/test_examples.py +++ b/Python/Examples/tests/test_examples.py @@ -1324,9 +1324,13 @@ def test_gnn_module_map(tmp_path, assert_root_hash, backend, hardware): ) # Verify output - output_file = tmp_path / "performance_track_finding.root" + output_file = tmp_path / "performance_finding_gnn.root" assert output_file.exists() - assert_root_hash("performance_track_finding.root", output_file) + assert_root_hash("performance_finding_gnn.root", output_file) + + output_file = tmp_path / "ntuple_finding_gnn.root" + assert output_file.exists() + assert_root_hash("ntuple_finding_gnn.root", output_file) @pytest.mark.odd From 8f497875134a14db9995ba3596d44d6b0dd157ee Mon Sep 17 00:00:00 2001 From: Benjamin Huth Date: Thu, 19 Mar 2026 17:38:25 +0100 Subject: [PATCH 2/6] lint --- Python/Examples/tests/test_examples.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/Examples/tests/test_examples.py b/Python/Examples/tests/test_examples.py index 1175e210004..f9c928f8876 100644 --- a/Python/Examples/tests/test_examples.py +++ b/Python/Examples/tests/test_examples.py @@ -1324,7 +1324,7 @@ def test_gnn_module_map(tmp_path, assert_root_hash, backend, hardware): output_file = tmp_path / "performance_finding_gnn.root" assert output_file.exists() assert_root_hash("performance_finding_gnn.root", output_file) - + output_file = tmp_path / "ntuple_finding_gnn.root" assert output_file.exists() assert_root_hash("ntuple_finding_gnn.root", output_file) From fa188ff96c5de51ac600b6f9cf45eee5681b05c3 Mon Sep 17 00:00:00 2001 From: Benjamin Huth Date: Thu, 19 Mar 2026 18:35:29 +0100 Subject: [PATCH 3/6] fix hash --- Python/Examples/tests/root_file_hashes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/Examples/tests/root_file_hashes.txt b/Python/Examples/tests/root_file_hashes.txt index 6f331187a76..3c9b4edb547 100644 --- a/Python/Examples/tests/root_file_hashes.txt +++ b/Python/Examples/tests/root_file_hashes.txt @@ -78,7 +78,7 @@ test_gnn_metric_learning[gpu]__performance_track_finding.root: 3f0fb36af55441994 test_gnn_module_map[gpu-torch]__performance_finding_gnn.root: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 test_gnn_module_map[gpu-torch]__ntuple_finding_gnn.root: 46e2577c4489d969c140a454c8ecaf7b22947324fc2df304e58a4929822c919f test_gnn_module_map[gpu-onnx]__performance_finding_gnn.root: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -test_gnn_module_map[gpu-onnx]__ntuple_finding_gnn.root: 46e2577c4489d969c140a454c8ecaf7b22947324fc2df304e58a4929822c919f +test_gnn_module_map[gpu-onnx]__ntuple_finding_gnn.root: 86ae901ef381dbb2fff872d4dacf598c51c030fb174ccb1ab7e3652e10a5185c test_ML_Ambiguity_Solver__performance_finding_ambiML.root: c17fb877bb165e28db0a2b99881763093e7fc9a707c045feb6a6a6b68e0dd660 test_truth_tracking_kalman[generic-0.0]__trackstates_kf.root: 42a49abcef0277ca061350cc03c1ac9841e119055e2778662ec6a731e316ecd2 test_truth_tracking_kalman[generic-0.0]__tracksummary_kf.root: bde974e07d033f9bb8e078c180d288c996f69590d6b4516625e1ec0276f327a7 From c4aee08aef52de00b2b4a49fed695df3bfa67831 Mon Sep 17 00:00:00 2001 From: Benjamin Huth Date: Fri, 20 Mar 2026 13:09:42 +0100 Subject: [PATCH 4/6] fix spacepoint - track association --- .../TrackFindingGnn/src/TrackFindingAlgorithmGnn.cpp | 2 +- Examples/Scripts/Python/gnn.py | 12 ++++++++++++ Examples/Scripts/Python/gnn4itk_example.py | 10 ++++++++++ Python/Examples/tests/test_examples.py | 12 +++++++----- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Examples/Algorithms/TrackFindingGnn/src/TrackFindingAlgorithmGnn.cpp b/Examples/Algorithms/TrackFindingGnn/src/TrackFindingAlgorithmGnn.cpp index 580f1de4754..3987871b913 100644 --- a/Examples/Algorithms/TrackFindingGnn/src/TrackFindingAlgorithmGnn.cpp +++ b/Examples/Algorithms/TrackFindingGnn/src/TrackFindingAlgorithmGnn.cpp @@ -226,7 +226,7 @@ ProcessCode TrackFindingAlgorithmGnn::execute( onetrack.reserve(candidate.size()); for (auto i : candidate) { - for (const auto& sl : spacePoints.at(i).sourceLinks()) { + for (const auto& sl : sortedSpacePoints.at(i).sourceLinks()) { onetrack.push_back(sl.template get().index()); } } diff --git a/Examples/Scripts/Python/gnn.py b/Examples/Scripts/Python/gnn.py index a9c02427a28..667d9234883 100755 --- a/Examples/Scripts/Python/gnn.py +++ b/Examples/Scripts/Python/gnn.py @@ -7,6 +7,7 @@ import acts import acts.examples import acts.examples.gnn +from acts.examples.simulation import addDigiParticleSelection, ParticleSelectorConfig from acts.examples.reconstruction import addGnn, addSpacePointsMaking from acts.examples.gnn import ( TorchMetricLearning, @@ -43,6 +44,17 @@ def runGnnMetricLearning( s=s, ) + addDigiParticleSelection( + s, + ParticleSelectorConfig( + pt=(1.0 * u.GeV, None), + eta=(-3.0, 3.0), + measurements=(7, None), + removeNeutral=True, + ), + ) + + addSpacePointsMaking( s, geoSelectionConfigFile=geometrySelection, diff --git a/Examples/Scripts/Python/gnn4itk_example.py b/Examples/Scripts/Python/gnn4itk_example.py index 4edb63e834f..d193b5eb357 100644 --- a/Examples/Scripts/Python/gnn4itk_example.py +++ b/Examples/Scripts/Python/gnn4itk_example.py @@ -16,6 +16,7 @@ import acts import acts.examples from acts.examples.reconstruction import addGnn +from acts.examples.simulation import ParticleSelectorConfig, addDigiParticleSelection from acts.examples.gnn import ( ModuleMapCuda, CudaTrackBuilding, @@ -81,6 +82,15 @@ def runGNN4ITk( ) ) + # Select primary particles with minimum 7 hits and 1 GeV pT for efficiency evaluation + s.addWhiteboardAlias("particles_simulated_selected", "particles") + particleSelectorConfig = ParticleSelectorConfig( + pt=(1.0 * u.GeV, None), + hits=(7, None), + removeSecondaries=True, + ) + addDigiParticleSelection(s, particleSelectorConfig, logLevel=logLevel) + # Configure GNN stages for module map workflow # All parameters hardcoded based on ITk configuration diff --git a/Python/Examples/tests/test_examples.py b/Python/Examples/tests/test_examples.py index 0c8306be11c..ad19cf5e1a8 100644 --- a/Python/Examples/tests/test_examples.py +++ b/Python/Examples/tests/test_examples.py @@ -1095,8 +1095,9 @@ def test_gnn_metric_learning(tmp_path, trk_geo, field, assert_root_hash, hardwar if hardware == "cpu": pytest.skip("CPU not yet supported") - root_file = "performance_track_finding.root" - assert not (tmp_path / root_file).exists() + root_files = ["performance_finding_gnn.root", "ntuple_finding_gnn.root"] + for f in root_files: + assert not (tmp_path / f).exists() # Check if models exist using MODEL_STORAGE environment variable model_storage = os.environ.get("MODEL_STORAGE") @@ -1133,10 +1134,11 @@ def test_gnn_metric_learning(tmp_path, trk_geo, field, assert_root_hash, hardwar print(e.output.decode("utf-8")) raise - rfp = tmp_path / root_file - assert rfp.exists() + for f in root_files: + rfp = tmp_path / f + assert rfp.exists() - assert_root_hash(root_file, rfp) + assert_root_hash(f, rfp) @pytest.mark.odd From 5e9a78a12b3c2cb27590ff70dd867c0486a75ad2 Mon Sep 17 00:00:00 2001 From: Benjamin Huth Date: Fri, 20 Mar 2026 13:10:23 +0100 Subject: [PATCH 5/6] lint --- Examples/Scripts/Python/gnn.py | 1 - Examples/Scripts/Python/gnn4itk_example.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Examples/Scripts/Python/gnn.py b/Examples/Scripts/Python/gnn.py index 667d9234883..eb9ac745135 100755 --- a/Examples/Scripts/Python/gnn.py +++ b/Examples/Scripts/Python/gnn.py @@ -54,7 +54,6 @@ def runGnnMetricLearning( ), ) - addSpacePointsMaking( s, geoSelectionConfigFile=geometrySelection, diff --git a/Examples/Scripts/Python/gnn4itk_example.py b/Examples/Scripts/Python/gnn4itk_example.py index d193b5eb357..712117c8eb6 100644 --- a/Examples/Scripts/Python/gnn4itk_example.py +++ b/Examples/Scripts/Python/gnn4itk_example.py @@ -82,7 +82,7 @@ def runGNN4ITk( ) ) - # Select primary particles with minimum 7 hits and 1 GeV pT for efficiency evaluation + # Select primary particles with minimum 7 hits and 1 GeV pT for efficiency evaluation s.addWhiteboardAlias("particles_simulated_selected", "particles") particleSelectorConfig = ParticleSelectorConfig( pt=(1.0 * u.GeV, None), From 1baf88b6a1ad4515b6a5b9131191799f50a26d86 Mon Sep 17 00:00:00 2001 From: Benjamin Huth Date: Fri, 20 Mar 2026 14:23:38 +0100 Subject: [PATCH 6/6] update hashes --- Python/Examples/tests/root_file_hashes.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Python/Examples/tests/root_file_hashes.txt b/Python/Examples/tests/root_file_hashes.txt index 3c9b4edb547..0323d8001d2 100644 --- a/Python/Examples/tests/root_file_hashes.txt +++ b/Python/Examples/tests/root_file_hashes.txt @@ -74,11 +74,12 @@ test_root_material_writer__material.root: e3b0c44298fc1c149afbf4c8996fb92427ae41 test_root_clusters_writer[configPosConstructor]__clusters.root: e842df4fe04eefff3df5f32cd1026e93286be62b8040dc700a2aff557c56dec8 test_root_clusters_writer[configKwConstructor]__clusters.root: e842df4fe04eefff3df5f32cd1026e93286be62b8040dc700a2aff557c56dec8 test_root_clusters_writer[kwargsConstructor]__clusters.root: e842df4fe04eefff3df5f32cd1026e93286be62b8040dc700a2aff557c56dec8 -test_gnn_metric_learning[gpu]__performance_track_finding.root: 3f0fb36af55441994a154ea2a93978ba1930d4e87bf043f8ae9527e283bf1894 +test_gnn_metric_learning[gpu]__performance_finding_gnn.root: c17fb877bb165e28db0a2b99881763093e7fc9a707c045feb6a6a6b68e0dd660 +test_gnn_metric_learning[gpu]__ntuple_finding_gnn.root: 3f0fb36af55441994a154ea2a93978ba1930d4e87bf043f8ae9527e283bf1894 +test_gnn_module_map[gpu-torch]__ntuple_finding_gnn.root: ae0c828b57f4d7b7e608fa92175af418647aece75b7ac1946bf30eb8e617d046 test_gnn_module_map[gpu-torch]__performance_finding_gnn.root: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -test_gnn_module_map[gpu-torch]__ntuple_finding_gnn.root: 46e2577c4489d969c140a454c8ecaf7b22947324fc2df304e58a4929822c919f +test_gnn_module_map[gpu-onnx]__ntuple_finding_gnn.root: 664be95535722685effd783b148ed2cec61906aee20f5a531bedbd5253d2b101 test_gnn_module_map[gpu-onnx]__performance_finding_gnn.root: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -test_gnn_module_map[gpu-onnx]__ntuple_finding_gnn.root: 86ae901ef381dbb2fff872d4dacf598c51c030fb174ccb1ab7e3652e10a5185c test_ML_Ambiguity_Solver__performance_finding_ambiML.root: c17fb877bb165e28db0a2b99881763093e7fc9a707c045feb6a6a6b68e0dd660 test_truth_tracking_kalman[generic-0.0]__trackstates_kf.root: 42a49abcef0277ca061350cc03c1ac9841e119055e2778662ec6a731e316ecd2 test_truth_tracking_kalman[generic-0.0]__tracksummary_kf.root: bde974e07d033f9bb8e078c180d288c996f69590d6b4516625e1ec0276f327a7