File indexing completed on 2026-09-10 08:20:39
0001
0002
0003 import os
0004 import argparse
0005 import pathlib
0006 import math
0007
0008 import acts
0009 import acts.examples
0010 from acts.examples.simulation import (
0011 addParticleGun,
0012 MomentumConfig,
0013 EtaConfig,
0014 PhiConfig,
0015 ParticleConfig,
0016 ParticleSelectorConfig,
0017 addPythia8,
0018 ParticleSelectorConfig,
0019 addGenParticleSelection,
0020 addFatras,
0021 addGeant4,
0022 addSimParticleSelection,
0023 addDigitization,
0024 addDigiParticleSelection,
0025 )
0026 from acts.examples.reconstruction import (
0027 addSeeding,
0028 CkfConfig,
0029 addCKFTracks,
0030 TrackSelectorConfig,
0031 addAmbiguityResolution,
0032 AmbiguityResolutionConfig,
0033 addAmbiguityResolutionML,
0034 AmbiguityResolutionMLConfig,
0035 addScoreBasedAmbiguityResolution,
0036 ScoreBasedAmbiguityResolutionConfig,
0037 addVertexFitting,
0038 VertexFinder,
0039 addSeedFilterML,
0040 SeedFilterMLDBScanConfig,
0041 )
0042 from acts.examples.odd import getOpenDataDetector, getOpenDataDetectorDirectory
0043
0044 u = acts.UnitConstants
0045
0046
0047 parser = argparse.ArgumentParser(description="Full chain with the OpenDataDetector")
0048 parser.add_argument(
0049 "--output",
0050 "-o",
0051 help="Output directory",
0052 type=pathlib.Path,
0053 default=pathlib.Path.cwd() / "odd_output",
0054 )
0055 parser.add_argument("--events", "-n", help="Number of events", type=int, default=100)
0056 parser.add_argument("--skip", "-s", help="Number of events", type=int, default=0)
0057 parser.add_argument("--edm4hep", help="Use edm4hep inputs", type=pathlib.Path)
0058 parser.add_argument(
0059 "--geant4", help="Use Geant4 instead of fatras", action="store_true"
0060 )
0061 parser.add_argument(
0062 "--ttbar",
0063 help="Use Pythia8 (ttbar, pile-up 200) instead of particle gun",
0064 action="store_true",
0065 )
0066 parser.add_argument(
0067 "--ttbar-pu",
0068 help="Number of pile-up events for ttbar",
0069 type=int,
0070 default=200,
0071 )
0072 parser.add_argument(
0073 "--gun-particles",
0074 help="Multiplicity (no. of particles) of the particle gun",
0075 type=int,
0076 default=4,
0077 )
0078 parser.add_argument(
0079 "--gun-multiplicity",
0080 help="Multiplicity (no. of vertices) of the particle gun",
0081 type=int,
0082 default=200,
0083 )
0084 parser.add_argument(
0085 "--gun-eta-range",
0086 nargs=2,
0087 help="Eta range of the particle gun",
0088 type=float,
0089 default=[-3.0, 3.0],
0090 )
0091 parser.add_argument(
0092 "--gun-pt-range",
0093 nargs=2,
0094 help="Pt range of the particle gun (GeV)",
0095 type=float,
0096 default=[1.0 * u.GeV, 10.0 * u.GeV],
0097 )
0098 parser.add_argument(
0099 "--digi-config", help="Digitization configuration file", type=pathlib.Path
0100 )
0101 parser.add_argument(
0102 "--material-config", help="Material map configuration file", type=pathlib.Path
0103 )
0104 parser.add_argument(
0105 "--ambi-solver",
0106 help="Set which ambiguity solver to use, default is the classical one",
0107 type=str,
0108 choices=["greedy", "scoring", "ML"],
0109 default="greedy",
0110 )
0111 parser.add_argument(
0112 "--ambi-config",
0113 help="Set the configuration file for the Score Based ambiguity resolution",
0114 type=pathlib.Path,
0115 default=pathlib.Path.cwd() / "ambi_config.json",
0116 )
0117
0118 parser.add_argument(
0119 "--MLSeedFilter",
0120 help="Use the Ml seed filter to select seed after the seeding step",
0121 action="store_true",
0122 )
0123 parser.add_argument(
0124 "--reco",
0125 help="Switch reco on/off",
0126 default=True,
0127 action=argparse.BooleanOptionalAction,
0128 )
0129 parser.add_argument(
0130 "--output-root",
0131 help="Switch root output on/off",
0132 default=True,
0133 action=argparse.BooleanOptionalAction,
0134 )
0135 parser.add_argument(
0136 "--output-csv",
0137 help="Switch csv output on/off",
0138 default=True,
0139 action=argparse.BooleanOptionalAction,
0140 )
0141 parser.add_argument(
0142 "--output-obj",
0143 help="Switch obj output on/off",
0144 default=True,
0145 action=argparse.BooleanOptionalAction,
0146 )
0147
0148 args = parser.parse_args()
0149
0150 outputDir = args.output
0151 ambi_ML = args.ambi_solver == "ML"
0152 ambi_scoring = args.ambi_solver == "scoring"
0153 ambi_config = args.ambi_config
0154 seedFilter_ML = args.MLSeedFilter
0155 geoDir = getOpenDataDetectorDirectory()
0156 actsDir = pathlib.Path(__file__).parent.parent.parent.parent
0157
0158
0159 oddMaterialMap = (
0160 args.material_config
0161 if args.material_config
0162 else geoDir / "data/odd-material-maps.root"
0163 )
0164
0165 oddDigiConfig = (
0166 args.digi_config
0167 if args.digi_config
0168 else actsDir / "Examples/Configs/odd-digi-smearing-config.json"
0169 )
0170
0171 oddSeedingSel = actsDir / "Examples/Configs/odd-seeding-config.json"
0172 oddMaterialDeco = acts.IMaterialDecorator.fromFile(oddMaterialMap)
0173
0174 detector = getOpenDataDetector(odd_dir=geoDir, materialDecorator=oddMaterialDeco)
0175 trackingGeometry = detector.trackingGeometry()
0176 decorators = detector.contextDecorators()
0177 field = acts.ConstantBField(acts.Vector3(0.0, 0.0, 2.0 * u.T))
0178 rnd = acts.examples.RandomNumbers(seed=42)
0179
0180 s = acts.examples.Sequencer(
0181 events=args.events,
0182 skip=args.skip,
0183 numThreads=1 if args.geant4 else -1,
0184 outputDir=str(outputDir),
0185 )
0186
0187 if args.edm4hep:
0188 import acts.examples.edm4hep
0189 from acts.examples.edm4hep import PodioReader
0190
0191 s.addReader(
0192 PodioReader(
0193 level=acts.logging.DEBUG,
0194 inputPath=str(args.edm4hep),
0195 outputFrame="events",
0196 category="events",
0197 )
0198 )
0199
0200 edm4hepReader = acts.examples.edm4hep.EDM4hepSimInputConverter(
0201 inputFrame="events",
0202 inputSimHits=[
0203 "PixelBarrelReadout",
0204 "PixelEndcapReadout",
0205 "ShortStripBarrelReadout",
0206 "ShortStripEndcapReadout",
0207 "LongStripBarrelReadout",
0208 "LongStripEndcapReadout",
0209 ],
0210 outputParticlesGenerator="particles_generated",
0211 outputParticlesSimulation="particles_simulated",
0212 outputSimHits="simhits",
0213 outputSimVertices="vertices_truth",
0214 dd4hepDetector=detector,
0215 trackingGeometry=trackingGeometry,
0216 sortSimHitsInTime=False,
0217 particleRMax=1080 * u.mm,
0218 particleZ=(-3030 * u.mm, 3030 * u.mm),
0219 particlePtMin=150 * u.MeV,
0220 level=acts.logging.DEBUG,
0221 )
0222 s.addAlgorithm(edm4hepReader)
0223
0224 s.addWhiteboardAlias("particles", edm4hepReader.config.outputParticlesSimulation)
0225
0226 addSimParticleSelection(
0227 s,
0228 ParticleSelectorConfig(
0229 rho=(0.0, 24 * u.mm),
0230 absZ=(0.0, 1.0 * u.m),
0231 eta=(-3.0, 3.0),
0232 removeNeutral=True,
0233 ),
0234 )
0235 else:
0236 if not args.ttbar:
0237 addParticleGun(
0238 s,
0239 MomentumConfig(
0240 args.gun_pt_range[0] * u.GeV,
0241 args.gun_pt_range[1] * u.GeV,
0242 transverse=True,
0243 ),
0244 EtaConfig(args.gun_eta_range[0], args.gun_eta_range[1]),
0245 PhiConfig(0.0, 360.0 * u.degree),
0246 ParticleConfig(
0247 args.gun_particles, acts.PdgParticle.eMuon, randomizeCharge=True
0248 ),
0249 vtxGen=acts.examples.GaussianDisplacedVertexPositionGenerator(
0250 rMean=50.0,
0251 rStdDev=50.0 * u.mm,
0252 zMean=2,
0253 zStdDev=55.5 * u.mm,
0254 tMean=0,
0255 tStdDev=1.0 * u.ns,
0256 ),
0257 multiplicity=args.gun_multiplicity,
0258 rnd=rnd,
0259 )
0260 else:
0261 addPythia8(
0262 s,
0263 hardProcess=["Top:qqbar2ttbar=on"],
0264 npileup=args.ttbar_pu,
0265 vtxGen=acts.examples.GaussianDisplacedVertexPositionGenerator(
0266 rMean=50.0,
0267 rStdDev=50.0 * u.mm,
0268 zMean=2,
0269 zStdDev=55.5 * u.mm,
0270 tMean=0,
0271 tStdDev=5.0 * u.ns,
0272 ),
0273 rnd=rnd,
0274 outputDirRoot=outputDir if args.output_root else None,
0275 outputDirCsv=outputDir if args.output_csv else None,
0276 )
0277
0278 addGenParticleSelection(
0279 s,
0280 ParticleSelectorConfig(
0281 rho=(0.0, 24 * u.mm),
0282 absZ=(0.0, 1.0 * u.m),
0283 eta=(-3.0, 3.0),
0284 pt=(150 * u.MeV, None),
0285 ),
0286 )
0287
0288 if args.geant4:
0289 if s.config.numThreads != 1:
0290 raise ValueError("Geant 4 simulation does not support multi-threading")
0291
0292
0293
0294
0295 addGeant4(
0296 s,
0297 detector,
0298 trackingGeometry,
0299 field,
0300 outputDirRoot=outputDir if args.output_root else None,
0301 outputDirCsv=outputDir if args.output_csv else None,
0302 outputDirObj=outputDir if args.output_obj else None,
0303 rnd=rnd,
0304 killVolume=trackingGeometry.highestTrackingVolume,
0305 killAfterTime=25 * u.ns,
0306 )
0307 else:
0308 addFatras(
0309 s,
0310 trackingGeometry,
0311 field,
0312 enableInteractions=True,
0313 outputDirRoot=outputDir if args.output_root else None,
0314 outputDirCsv=outputDir if args.output_csv else None,
0315 outputDirObj=outputDir if args.output_obj else None,
0316 rnd=rnd,
0317 )
0318
0319 addDigitization(
0320 s,
0321 trackingGeometry,
0322 field,
0323 digiConfigFile=oddDigiConfig,
0324 outputDirRoot=outputDir if args.output_root else None,
0325 outputDirCsv=outputDir if args.output_csv else None,
0326 rnd=rnd,
0327 )
0328
0329 addDigiParticleSelection(
0330 s,
0331 ParticleSelectorConfig(
0332 pt=(1.0 * u.GeV, None),
0333 eta=(-3.0, 3.0),
0334 measurements=(9, None),
0335 removeNeutral=True,
0336 ),
0337 )
0338
0339 if args.reco:
0340 addSeeding(
0341 s,
0342 trackingGeometry,
0343 field,
0344 initialSigmas=[
0345 1 * u.mm,
0346 1 * u.mm,
0347 1 * u.degree,
0348 1 * u.degree,
0349 0 * u.e / u.GeV,
0350 1 * u.ns,
0351 ],
0352 initialSigmaQoverPt=0.1 * u.e / u.GeV,
0353 initialSigmaPtRel=0.1,
0354 initialVarInflation=[1.0] * 6,
0355 particleHypothesis=acts.ParticleHypothesis.muon,
0356 geoSelectionConfigFile=oddSeedingSel,
0357 outputDirRoot=outputDir if args.output_root else None,
0358 outputDirCsv=outputDir if args.output_csv else None,
0359 logLevel=acts.logging.DEBUG,
0360 )
0361
0362 if seedFilter_ML:
0363 addSeedFilterML(
0364 s,
0365 SeedFilterMLDBScanConfig(
0366 epsilonDBScan=0.03, minPointsDBScan=2, minSeedScore=0.1
0367 ),
0368 onnxModelFile=os.path.dirname(__file__)
0369 + "/MLAmbiguityResolution/seedDuplicateClassifier.onnx",
0370 outputDirRoot=outputDir if args.output_root else None,
0371 outputDirCsv=outputDir if args.output_csv else None,
0372 )
0373
0374 addCKFTracks(
0375 s,
0376 trackingGeometry,
0377 field,
0378 TrackSelectorConfig(
0379 pt=(1.0 * u.GeV if args.ttbar else 0.0, None),
0380 absEta=(None, 3.0),
0381 loc0=(-4.0 * u.mm, 4.0 * u.mm),
0382 nMeasurementsMin=7,
0383 maxHoles=2,
0384 maxOutliers=2,
0385 ),
0386 CkfConfig(
0387 chi2CutOffMeasurement=15.0,
0388 chi2CutOffOutlier=25.0,
0389 numMeasurementsCutOff=2,
0390 seedDeduplication=True,
0391 stayOnSeed=True,
0392 pixelVolumes=[16, 17, 18],
0393 stripVolumes=[23, 24, 25],
0394 maxPixelHoles=1,
0395 maxStripHoles=2,
0396 constrainToVolumes=[
0397 2,
0398 32,
0399 4,
0400 16,
0401 17,
0402 18,
0403 20,
0404 23,
0405 24,
0406 25,
0407 26,
0408 8,
0409 28,
0410 29,
0411 30,
0412 ],
0413 ),
0414 outputDirRoot=outputDir if args.output_root else None,
0415 outputDirCsv=outputDir if args.output_csv else None,
0416 writeCovMat=True,
0417 logLevel=acts.logging.DEBUG,
0418 )
0419
0420 if ambi_ML:
0421 addAmbiguityResolutionML(
0422 s,
0423 AmbiguityResolutionMLConfig(
0424 maximumSharedHits=3, maximumIterations=1000000, nMeasurementsMin=7
0425 ),
0426 outputDirRoot=outputDir if args.output_root else None,
0427 outputDirCsv=outputDir if args.output_csv else None,
0428 onnxModelFile=os.path.dirname(__file__)
0429 + "/MLAmbiguityResolution/duplicateClassifier.onnx",
0430 )
0431
0432 elif ambi_scoring:
0433 addScoreBasedAmbiguityResolution(
0434 s,
0435 ScoreBasedAmbiguityResolutionConfig(
0436 minScore=0,
0437 minScoreSharedTracks=1,
0438 maxShared=2,
0439 minUnshared=3,
0440 maxSharedTracksPerMeasurement=2,
0441 useAmbiguityScoring=False,
0442 ),
0443 outputDirRoot=outputDir if args.output_root else None,
0444 outputDirCsv=outputDir if args.output_csv else None,
0445 ambiVolumeFile=ambi_config,
0446 writeCovMat=True,
0447 )
0448 else:
0449 addAmbiguityResolution(
0450 s,
0451 AmbiguityResolutionConfig(
0452 maximumSharedHits=3, maximumIterations=1000000, nMeasurementsMin=7
0453 ),
0454 outputDirRoot=outputDir if args.output_root else None,
0455 outputDirCsv=outputDir if args.output_csv else None,
0456 writeCovMat=True,
0457 )
0458
0459 addVertexFitting(
0460 s,
0461 field,
0462 vertexFinder=VertexFinder.AMVF,
0463 outputDirRoot=outputDir if args.output_root else None,
0464 outputDirCsv=outputDir if args.output_csv else None,
0465 )
0466
0467 s.run()