File indexing completed on 2026-07-16 08:08:21
0001
0002
0003 """
0004 Example: GNN track finding with module maps on ATLAS ITk data.
0005
0006 Demonstrates reading pre-simulated ATLAS data and running GNN with module map
0007 (geometry-based) graph construction. Typical workflow for ATLAS ITk.
0008
0009 All parameters are hardcoded except file paths. Users should copy and modify this script
0010 for their specific needs.
0011 """
0012
0013 from pathlib import Path
0014 import argparse
0015
0016 import acts
0017 import acts.examples
0018 from acts.examples.reconstruction import addGnn
0019 from acts.examples.gnn import (
0020 ModuleMapCuda,
0021 CudaTrackBuilding,
0022 NodeFeature,
0023 )
0024
0025 u = acts.UnitConstants
0026
0027
0028 def runGNN4ITk(
0029 inputRootDump: Path,
0030 moduleMapPath: str,
0031 gnnModel: Path,
0032 outputDir: Path = Path.cwd(),
0033 events: int = 1,
0034 logLevel=acts.logging.INFO,
0035 ):
0036 """
0037 Run GNN tracking with module maps on ATLAS Athena dumps.
0038
0039 This example shows reading pre-simulated ATLAS data and running GNN with
0040 geometry-based graph construction. All GNN parameters hardcoded.
0041
0042 Args:
0043 inputRootDump: Path to input ROOT file (ATLAS Athena dump format)
0044 moduleMapPath: Path prefix for module map files
0045 (will load .doublets.root and .triplets.root)
0046 gnnModel: Path to trained model (.pt, .onnx, or .engine)
0047 outputDir: Output directory for performance files
0048 events: Number of events to process
0049 logLevel: Logging level
0050 """
0051
0052 assert inputRootDump.exists(), f"Input file not found: {inputRootDump}"
0053 assert Path(
0054 moduleMapPath + ".doublets.root"
0055 ).exists(), f"Module map not found: {moduleMapPath}.doublets.root"
0056 assert Path(
0057 moduleMapPath + ".triplets.root"
0058 ).exists(), f"Module map not found: {moduleMapPath}.triplets.root"
0059 assert gnnModel.exists(), f"Model file not found: {gnnModel}"
0060
0061 s = acts.examples.Sequencer(
0062 events=events,
0063 numThreads=1,
0064 )
0065
0066
0067 s.addReader(
0068 acts.examples.root.RootAthenaDumpReader(
0069 level=logLevel,
0070 treename="GNN4ITk",
0071 inputfiles=[str(inputRootDump)],
0072 outputSpacePoints="spacepoints",
0073 outputClusters="clusters",
0074 outputMeasurements="measurements",
0075 outputMeasurementParticlesMap="measurement_particles_map",
0076 outputParticleMeasurementsMap="particle_measurements_map",
0077 outputParticles="particles",
0078 skipOverlapSPsPhi=True,
0079 skipOverlapSPsEta=False,
0080 absBoundaryTolerance=0.01 * u.mm,
0081 )
0082 )
0083
0084
0085
0086
0087
0088 moduleMapConfig = {
0089 "level": logLevel,
0090 "moduleMapPath": moduleMapPath,
0091 "rScale": 1000.0,
0092 "phiScale": 3.141592654,
0093 "zScale": 1000.0,
0094 "etaScale": 1.0,
0095 "gpuDevice": 0,
0096 "gpuBlocks": 512,
0097 "moreParallel": True,
0098 }
0099 graphConstructor = ModuleMapCuda(**moduleMapConfig)
0100
0101
0102 gnnModel = Path(gnnModel)
0103 edgeClassifierConfig = {
0104 "level": logLevel,
0105 "modelPath": str(gnnModel),
0106 "cut": 0.5,
0107 }
0108
0109 if gnnModel.suffix == ".pt":
0110 edgeClassifierConfig["useEdgeFeatures"] = True
0111 from acts.examples.gnn import TorchEdgeClassifier
0112
0113 edgeClassifiers = [TorchEdgeClassifier(**edgeClassifierConfig)]
0114 elif gnnModel.suffix == ".onnx":
0115 from acts.examples.gnn import OnnxEdgeClassifier
0116
0117 edgeClassifiers = [OnnxEdgeClassifier(**edgeClassifierConfig)]
0118 elif gnnModel.suffix == ".engine":
0119 from acts.examples.gnn import TensorRTEdgeClassifier
0120
0121 edgeClassifiers = [TensorRTEdgeClassifier(**edgeClassifierConfig)]
0122 else:
0123 raise ValueError(f"Unsupported model format: {gnnModel.suffix}")
0124
0125
0126 trackBuilderConfig = {
0127 "level": logLevel,
0128 "useOneBlockImplementation": False,
0129 "doJunctionRemoval": True,
0130 }
0131 trackBuilder = CudaTrackBuilding(**trackBuilderConfig)
0132
0133
0134 e = NodeFeature
0135 nodeFeatures = [
0136 e.R,
0137 e.Phi,
0138 e.Z,
0139 e.Eta,
0140 e.Cluster1R,
0141 e.Cluster1Phi,
0142 e.Cluster1Z,
0143 e.Cluster1Eta,
0144 e.Cluster2R,
0145 e.Cluster2Phi,
0146 e.Cluster2Z,
0147 e.Cluster2Eta,
0148 ]
0149 featureScales = [1000.0, 3.141592654, 1000.0, 1.0] * 3
0150
0151
0152 addGnn(
0153 s,
0154 graphConstructor=graphConstructor,
0155 edgeClassifiers=edgeClassifiers,
0156 trackBuilder=trackBuilder,
0157 nodeFeatures=nodeFeatures,
0158 featureScales=featureScales,
0159 inputSpacePoints="spacepoints",
0160 inputClusters="clusters",
0161 outputDirRoot=str(outputDir),
0162 logLevel=logLevel,
0163 )
0164
0165 s.run()
0166 return s
0167
0168
0169 if __name__ == "__main__":
0170 argparser = argparse.ArgumentParser(
0171 description="Run GNN track finding with module maps on ATLAS data"
0172 )
0173
0174 argparser.add_argument(
0175 "--inputRootDump",
0176 type=Path,
0177 required=True,
0178 help="Path to the input ROOT dump file (ATLAS Athena format)",
0179 )
0180 argparser.add_argument(
0181 "--moduleMapPath",
0182 type=str,
0183 required=True,
0184 help="Path prefix for module map files (without .doublets.root/.triplets.root)",
0185 )
0186 argparser.add_argument(
0187 "--gnnModel",
0188 type=Path,
0189 required=True,
0190 help="Path to the GNN model file (.pt, .onnx, or .engine)",
0191 )
0192 argparser.add_argument(
0193 "--outputDir",
0194 type=Path,
0195 default=Path.cwd(),
0196 help="Output directory for performance files",
0197 )
0198 argparser.add_argument(
0199 "--events",
0200 type=int,
0201 default=1,
0202 help="Number of events to process",
0203 )
0204
0205 args = argparser.parse_args()
0206
0207 runGNN4ITk(
0208 inputRootDump=args.inputRootDump,
0209 moduleMapPath=args.moduleMapPath,
0210 gnnModel=args.gnnModel,
0211 outputDir=args.outputDir,
0212 events=args.events,
0213 )