Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2026-07-16 08:08:21

0001 #!/usr/bin/env python3
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     # Validate inputs
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     # Read ATLAS Athena ROOT dump
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     # Configure GNN stages for module map workflow
0085     # All parameters hardcoded based on ITk configuration
0086 
0087     # Stage 1: Graph construction via module map
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     # Stage 2: Single-stage edge classification (auto-detect backend)
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     # Stage 3: GPU track building
0126     trackBuilderConfig = {
0127         "level": logLevel,
0128         "useOneBlockImplementation": False,
0129         "doJunctionRemoval": True,
0130     }
0131     trackBuilder = CudaTrackBuilding(**trackBuilderConfig)
0132 
0133     # Node features: ITk 12-feature configuration (spacepoint + 2 clusters)
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     # Add GNN tracking (spacepoints already created by reader, so no trackingGeometry)
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     )