FairShip
Loading...
Searching...
No Matches
run_tracking_benchmark.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# SPDX-License-Identifier: LGPL-3.0-or-later
3# SPDX-FileCopyrightText: Copyright CERN for the benefit of the SHiP Collaboration
4
5"""Run a full tracking benchmark: simulation -> reconstruction -> metrics.
6
7Fires a particle gun upstream of the straw tube spectrometer (T1-T4),
8runs digitisation and reconstruction with template matching pattern
9recognition, then computes tracking performance metrics.
10
11Each phase (sim, reco) runs as a subprocess because FairRoot singletons
12prevent creating multiple FairRunSim instances in the same process.
13
14Example usage:
15 python macro/run_tracking_benchmark.py -n 200 --seed 42 --tag test
16 python macro/run_tracking_benchmark.py -n 1000 --nTracks 5 --tag multi
17"""
18
19from __future__ import annotations
20
21import os
22import subprocess
23import sys
24from argparse import ArgumentParser
25
26parser = ArgumentParser(description="Tracking performance benchmark for straw tube spectrometer")
27parser.add_argument("-n", "--nEvents", type=int, default=1000, help="Number of events (default: 1000)")
28parser.add_argument("--pID", type=int, default=13, help="Particle PDG ID (default: 13, mu-)")
29parser.add_argument("--Estart", type=float, default=1.0, help="Start of energy range in GeV (default: 1)")
30parser.add_argument("--Eend", type=float, default=100.0, help="End of energy range in GeV (default: 100)")
31parser.add_argument("--Vz", type=float, default=8300.0, help="Gun z-position in cm (default: 8300, ~1m upstream of T1)")
32parser.add_argument("--Dx", type=float, default=200.0, help="Position spread in x [cm] (default: 200)")
33parser.add_argument("--Dy", type=float, default=300.0, help="Position spread in y [cm] (default: 300)")
34parser.add_argument("--nTracks", type=int, default=1, help="Tracks per event (default: 1)")
35parser.add_argument("--tag", default="benchmark", help="Output file tag (default: benchmark)")
36parser.add_argument("--output-json", default=None, help="JSON metrics output path")
37parser.add_argument("--seed", type=int, default=42, help="Random seed (default: 42)")
38parser.add_argument("-o", "--outputDir", default=".", help="Output directory (default: .)")
39parser.add_argument(
40 "--debug",
41 type=int,
42 default=0,
43 choices=range(0, 4),
44 help="FairLogger verbosity: 0=info, 1=debug, 2=debug1, 3=debug2",
45)
46
47options = parser.parse_args()
48
49if not os.path.exists(options.outputDir):
50 os.makedirs(options.outputDir)
51
52tag = options.tag
53sim_file = f"{options.outputDir}/sim_{tag}.root"
54geo_file = f"{options.outputDir}/geo_{tag}.root"
55reco_file = f"{options.outputDir}/sim_{tag}_rec.root"
56json_file = options.output_json or f"{options.outputDir}/tracking_metrics.json"
57histo_file = f"{options.outputDir}/tracking_benchmark_histos.root"
58
59fairship = os.environ.get("FAIRSHIP", "")
60
61
62def run_phase(description: str, cmd: list[str]) -> None:
63 """Run a subprocess phase, raising on failure."""
64 print("=" * 60)
65 print(f"{description}")
66 print("=" * 60)
67 result = subprocess.run(cmd, check=False)
68 if result.returncode != 0:
69 print(f"FAILED: {description} (exit code {result.returncode})")
70 sys.exit(result.returncode)
71
72
73# ============================================================
74# Phase 1: Simulation via run_simScript.py
75# ============================================================
76sim_script = os.path.join(fairship, "macro", "run_simScript.py") if fairship else "macro/run_simScript.py"
77sim_cmd = [
78 sys.executable,
79 sim_script,
80 "-n",
81 str(options.nEvents),
82 "-s",
83 str(options.seed),
84 "--debug",
85 str(options.debug),
86 "--vacuums",
87 "--SND",
88 "--SND_design",
89 "2",
90 "--shieldName",
91 "TRY_2025",
92 "--tag",
93 tag,
94 "-o",
95 options.outputDir,
96 "PG",
97 "--pID",
98 str(options.pID),
99 "--Estart",
100 str(options.Estart),
101 "--Eend",
102 str(options.Eend),
103 "--Vz",
104 str(options.Vz),
105 "--multiplePG",
106 "--Dx",
107 str(options.Dx),
108 "--Dy",
109 str(options.Dy),
110]
111
112run_phase("Phase 1: Simulation", sim_cmd)
113
114if not os.path.exists(sim_file):
115 print(f"ERROR: Simulation output {sim_file} not found")
116 sys.exit(1)
117
118# ============================================================
119# Phase 2: Reconstruction via ShipReco.py
120# ============================================================
121reco_script = os.path.join(fairship, "macro", "ShipReco.py") if fairship else "macro/ShipReco.py"
122reco_cmd = [
123 sys.executable,
124 reco_script,
125 "-f",
126 sim_file,
127 "-g",
128 geo_file,
129 "-n",
130 str(options.nEvents),
131 "--realPR",
132 "AR",
133]
134if options.debug > 0:
135 reco_cmd.append("--Debug")
136
137run_phase("Phase 2: Reconstruction", reco_cmd)
138
139if not os.path.exists(reco_file):
140 print(f"ERROR: Reconstruction output {reco_file} not found")
141 sys.exit(1)
142
143# ============================================================
144# Phase 3: Metrics (runs in this process — no FairRunSim needed)
145# ============================================================
146print("=" * 60)
147print("Phase 3: Benchmark Metrics")
148print("=" * 60)
149
150import tracking_benchmark
151
152bench = tracking_benchmark.TrackingBenchmark(sim_file, reco_file, geo_file)
153bench.compute_metrics()
154bench.print_summary()
155bench.save_json(json_file)
156bench.save_histograms(histo_file)
157
158print("=" * 60)
159print("Tracking benchmark complete.")
160print(f" Metrics: {json_file}")
161print(f" Histograms: {histo_file}")
162print("=" * 60)
None run_phase(str description, list[str] cmd)