File indexing completed on 2026-04-03 07:48:19
0001
0002
0003 import sys, os, argparse, pathlib
0004
0005
0006 def parse_args():
0007 from acts.examples.reconstruction import SeedingAlgorithm
0008
0009 parser = argparse.ArgumentParser(description="""
0010 Script to test the full chain ACTS simulation and reconstruction.
0011
0012 This script is provided for interactive developer testing only.
0013 It is not intended (and not supported) for end user use, automated testing,
0014 and certainly should never be called in production. The Python API is the
0015 proper way to access the ActsExamples from scripts. The other Examples/Scripts
0016 are much better examples of how to do that. physmon in the CI is the proper
0017 way to do automated integration tests. This script is only for the case of
0018 interactive testing with one-off configuration specified by command-line options.
0019 """)
0020 parser.add_argument(
0021 "-G",
0022 "--generic-detector",
0023 action="store_true",
0024 help="Use generic detector geometry and config",
0025 )
0026 parser.add_argument(
0027 "--odd",
0028 default=True,
0029 action=argparse.BooleanOptionalAction,
0030 help="Use Open Data Detector geometry and config (default unless overridden by -G or -A). Requires ACTS_BUILD_ODD.",
0031 )
0032 parser.add_argument(
0033 "-A",
0034 "--itk",
0035 action="store_true",
0036 help="Use ATLAS ITk geometry and config. Requires acts-itk/ in current directory.",
0037 )
0038 parser.add_argument(
0039 "-g",
0040 "--geant4",
0041 action="store_true",
0042 help="Use Geant4 instead of Fatras for detector simulation",
0043 )
0044 parser.add_argument(
0045 "--edm4hep",
0046 type=pathlib.Path,
0047 help="Use edm4hep inputs",
0048 )
0049 parser.add_argument(
0050 "-b",
0051 "--bf-constant",
0052 action="store_true",
0053 help="Use constant 2T B-field also for ITk; and don't include material map",
0054 )
0055 parser.add_argument(
0056 "-j",
0057 "--threads",
0058 type=int,
0059 default=-1,
0060 help="Number of parallel threads, negative for automatic (default).",
0061 )
0062 parser.add_argument(
0063 "-o",
0064 "--output-dir",
0065 "--output",
0066 default=None,
0067 type=pathlib.Path,
0068 help="Directory to write outputs to",
0069 )
0070 parser.add_argument(
0071 "-O",
0072 "--output-detail",
0073 action="count",
0074 default=0,
0075 help="fewer output files. Use -OO for more output files. Use -OOO to disable all output.",
0076 )
0077 parser.add_argument(
0078 "-c",
0079 "--output-csv",
0080 action="count",
0081 default=0,
0082 help="Use CSV output instead of ROOT. Specify -cc to output all formats (ROOT, CSV, and obj).",
0083 )
0084 parser.add_argument(
0085 "--output-obj",
0086 action="store_true",
0087 help="Enable obj output",
0088 )
0089 parser.add_argument(
0090 "-n",
0091 "--events",
0092 type=int,
0093 default=100,
0094 help="The number of events to process (default=%(default)d).",
0095 )
0096 parser.add_argument(
0097 "-s",
0098 "--skip",
0099 type=int,
0100 default=0,
0101 help="Number of events to skip (default=%(default)d)",
0102 )
0103
0104
0105 parser.add_argument(
0106 "-N",
0107 "--gen-nparticles",
0108 "--gun-particles",
0109 type=int,
0110 default=4,
0111 help="Number of generated particles per vertex from the particle gun (default=%(default)d).",
0112 )
0113 parser.add_argument(
0114 "-M",
0115 "--gen-nvertices",
0116 "--gun-multiplicity",
0117 "--ttbar-pu",
0118 type=int,
0119 default=200,
0120 help="Number of vertices per event (multiplicity) from the particle gun; or number of pileup events (default=%(default)d)",
0121 )
0122 parser.add_argument(
0123 "-t",
0124 "--ttbar-pu200",
0125 "--ttbar",
0126 action="store_true",
0127 help="Generate ttbar + mu=200 pile-up using Pythia8",
0128 )
0129 parser.add_argument(
0130 "-p",
0131 "--gen-pt-range",
0132 "--gun-pt-range",
0133 default="1:10",
0134 help="transverse momentum (pT) range (min:max) of the particle gun in GeV (default=%(default)s)",
0135 )
0136 parser.add_argument(
0137 "--gen-eta-range",
0138 "--gun-eta-range",
0139 help="Eta range (min:max) of the particle gun (default -2:2 (Generic), -3:3 (ODD), -4:4 (ITk))",
0140 )
0141 parser.add_argument(
0142 "--gen-cos-theta",
0143 action="store_true",
0144 help="Sample eta as cos(theta) and not uniform",
0145 )
0146 parser.add_argument(
0147 "-r",
0148 "--random-seed",
0149 type=int,
0150 default=42,
0151 help="Random number seed (default=%(default)d)",
0152 )
0153 parser.add_argument(
0154 "-F",
0155 "--disable-fpemon",
0156 action="store_true",
0157 help="sets ACTS_SEQUENCER_DISABLE_FPEMON=1",
0158 )
0159 parser.add_argument(
0160 "-l",
0161 "--loglevel",
0162 type=int,
0163 default=2,
0164 help="The output log level. Please set the wished number (0 = VERBOSE, 1 = DEBUG, 2 = INFO (default), 3 = WARNING, 4 = ERROR, 5 = FATAL).",
0165 )
0166 parser.add_argument(
0167 "-d",
0168 "--dump-args-calls",
0169 action="store_true",
0170 help="Show pybind function call details",
0171 )
0172 parser.add_argument(
0173 "--digi-config",
0174 type=pathlib.Path,
0175 help="Digitization configuration file",
0176 )
0177 parser.add_argument(
0178 "--material-config",
0179 type=pathlib.Path,
0180 help="Material map configuration file",
0181 )
0182 parser.add_argument(
0183 "-S",
0184 "--seeding-algorithm",
0185 action=EnumAction,
0186 enum=SeedingAlgorithm,
0187 default=SeedingAlgorithm.GridTriplet,
0188 help="Select the seeding algorithm to use",
0189 )
0190 parser.add_argument(
0191 "--ckf",
0192 default=True,
0193 action=argparse.BooleanOptionalAction,
0194 help="Switch CKF on/off",
0195 )
0196 parser.add_argument(
0197 "--reco",
0198 default=True,
0199 action=argparse.BooleanOptionalAction,
0200 help="Switch reco on/off",
0201 )
0202 parser.add_argument(
0203 "--vertexing",
0204 default=True,
0205 action=argparse.BooleanOptionalAction,
0206 help="Switch vertexing on/off",
0207 )
0208 parser.add_argument(
0209 "--MLSeedFilter",
0210 action="store_true",
0211 help="Use the ML seed filter to select seed after the seeding step",
0212 )
0213 parser.add_argument(
0214 "--ambi-solver",
0215 type=str,
0216 choices=["greedy", "scoring", "ML", "none"],
0217 default="greedy",
0218 help="Set which ambiguity solver to use (default=%(default)s)",
0219 )
0220 parser.add_argument(
0221 "--ambi-config",
0222 type=pathlib.Path,
0223 default=pathlib.Path.cwd() / "ambi_config.json",
0224 help="Set the configuration file for the Score Based ambiguity resolution (default=%(default)s)",
0225 )
0226 return parser.parse_args()
0227
0228
0229 def full_chain(args):
0230 import acts, acts.root
0231
0232
0233 global detector, trackingGeometry, decorators, field, rnd
0234 global logger
0235
0236 if args.disable_fpemon:
0237 os.environ["ACTS_SEQUENCER_DISABLE_FPEMON"] = "1"
0238
0239 if args.dump_args_calls:
0240 acts.examples.dump_args_calls(locals())
0241
0242 logger = acts.getDefaultLogger("full_chain_test", acts.logging.Level(args.loglevel))
0243
0244 nDetArgs = [args.generic_detector, args.odd, args.itk].count(True)
0245 if nDetArgs == 0:
0246 args.generic_detector = True
0247 elif nDetArgs == 2:
0248 args.odd = False
0249 nDetArgs = [args.generic_detector, args.odd, args.itk].count(True)
0250 if nDetArgs != 1:
0251 logger.fatal("require exactly one of: --generic-detector --odd --itk")
0252 sys.exit(2)
0253 if args.generic_detector:
0254 detname = "gen"
0255 elif args.itk:
0256 detname = "itk"
0257 elif args.odd:
0258 detname = "odd"
0259
0260 u = acts.UnitConstants
0261
0262 if args.output_detail == 3:
0263 outputDirLess = None
0264 elif args.output_dir is None:
0265 outputDirLess = pathlib.Path.cwd() / f"{detname}_output"
0266 else:
0267 outputDirLess = args.output_dir
0268
0269 outputDir = None if args.output_detail == 1 else outputDirLess
0270 outputDirMore = None if args.output_detail in (0, 1) else outputDirLess
0271
0272 outputDirRoot = outputDir if args.output_csv != 1 else None
0273 outputDirLessRoot = outputDirLess if args.output_csv != 1 else None
0274 outputDirMoreRoot = outputDirMore if args.output_csv != 1 else None
0275 outputDirCsv = outputDir if args.output_csv != 0 else None
0276 outputDirLessCsv = outputDirLess if args.output_csv != 0 else None
0277 outputDirMoreCsv = outputDirMore if args.output_csv != 0 else None
0278 outputDirObj = (
0279 outputDirLess
0280 if args.output_obj
0281 else outputDir if args.output_csv == 2 else None
0282 )
0283
0284 actsDir = pathlib.Path(__file__).resolve().parent.parent.parent.parent
0285
0286
0287 if args.generic_detector:
0288 etaRange = (-2.0, 2.0)
0289 ptMin = 0.5 * u.GeV
0290 rhoMax = 24.0 * u.mm
0291 if args.loglevel <= 2:
0292 logger.info(f"Load Generic Detector from {actsDir}")
0293 if args.digi_config is None:
0294 args.digi_config = actsDir / "Examples/Configs/generic-digi-smearing-config.json"
0295 seedingConfigFile = actsDir / "Examples/Configs/generic-seeding-config.json"
0296 args.bf_constant = True
0297 detector = acts.examples.GenericDetector()
0298 trackingGeometry = detector.trackingGeometry()
0299 decorators = detector.contextDecorators()
0300 elif args.odd:
0301 import acts.examples.odd
0302 etaRange = (-3.0, 3.0)
0303 ptMin = 1.0 * u.GeV
0304 rhoMax = 24.0 * u.mm
0305 geoDir = acts.examples.odd.getOpenDataDetectorDirectory()
0306 if args.loglevel <= 2:
0307 logger.info(f"Load Open Data Detector from {geoDir.resolve()}")
0308 if args.digi_config is None:
0309 args.digi_config = actsDir / "Examples/Configs/odd-digi-smearing-config.json"
0310 seedingConfigFile = actsDir / "Examples/Configs/odd-seeding-config.json"
0311 if args.material_config is None:
0312 args.material_config = geoDir / "data/odd-material-maps.root"
0313 args.bf_constant = True
0314 detector = acts.examples.odd.getOpenDataDetector(
0315 odd_dir=geoDir,
0316 materialDecorator=acts.IMaterialDecorator.fromFile(args.material_config),
0317 )
0318 trackingGeometry = detector.trackingGeometry()
0319 decorators = detector.contextDecorators()
0320 elif args.itk:
0321 import acts.examples.itk as itk
0322 etaRange = (-4.0, 4.0)
0323 ptMin = 1.0 * u.GeV
0324 rhoMax = 28.0 * u.mm
0325 geoDir = pathlib.Path("acts-itk")
0326 if args.loglevel <= 2:
0327 logger.info(f"Load ATLAS ITk from {geoDir.resolve()}")
0328 if args.digi_config is None:
0329 args.digi_config = geoDir / "itk-hgtd/itk-smearing-config.json"
0330 seedingConfigFile = geoDir / "itk-hgtd/geoSelection-ITk.json"
0331
0332 bFieldFile = geoDir / "bfield/ATLAS-BField-xyz.root"
0333 detector = itk.buildITkGeometry(
0334 geoDir,
0335 customMaterialFile=args.material_config,
0336 material=not args.bf_constant,
0337 logLevel=acts.logging.Level(args.loglevel),
0338 )
0339 trackingGeometry = detector.trackingGeometry()
0340 decorators = detector.contextDecorators()
0341
0342
0343 if args.bf_constant:
0344 field = acts.ConstantBField(acts.Vector3(0.0, 0.0, 2.0 * u.T))
0345 else:
0346 logger.info("Create magnetic field map from {}", bFieldFile)
0347 field = acts.root.MagneticFieldMapXyz(str(bFieldFile))
0348 rnd = acts.examples.RandomNumbers(seed=42)
0349
0350 from acts.examples.simulation import (
0351 MomentumConfig,
0352 EtaConfig,
0353 PhiConfig,
0354 ParticleConfig,
0355 ParticleSelectorConfig,
0356 addDigitization,
0357 addGenParticleSelection,
0358 addSimParticleSelection,
0359 addDigiParticleSelection,
0360 )
0361
0362 s = acts.examples.Sequencer(
0363 events=args.events,
0364 skip=args.skip,
0365 numThreads=args.threads if not (args.geant4 and args.threads == -1) else 1,
0366 logLevel=acts.logging.Level(args.loglevel),
0367 outputDir="" if outputDirLess is None else str(outputDirLess),
0368 )
0369
0370
0371 for d in decorators:
0372 s.addContextDecorator(d)
0373
0374 if args.edm4hep:
0375 import acts.examples.edm4hep
0376
0377 s.addReader(
0378 acts.examples.edm4hep.PodioReader(
0379 level=acts.logging.DEBUG,
0380 inputPath=str(args.edm4hep),
0381 outputFrame="events",
0382 category="events",
0383 )
0384 )
0385
0386 edm4hepReader = acts.examples.edm4hep.EDM4hepSimInputConverter(
0387 inputFrame="events",
0388 inputSimHits=[
0389 "PixelBarrelReadout",
0390 "PixelEndcapReadout",
0391 "ShortStripBarrelReadout",
0392 "ShortStripEndcapReadout",
0393 "LongStripBarrelReadout",
0394 "LongStripEndcapReadout",
0395 ],
0396 outputParticlesGenerator="particles_generated",
0397 outputParticlesSimulation="particles_simulated",
0398 outputSimHits="simhits",
0399 outputSimVertices="vertices_truth",
0400 dd4hepDetector=detector,
0401 trackingGeometry=trackingGeometry,
0402 sortSimHitsInTime=False,
0403 particleRMax=1080 * u.mm,
0404 particleZ=(-3030 * u.mm, 3030 * u.mm),
0405 particlePtMin=150 * u.MeV,
0406 level=acts.logging.DEBUG,
0407 )
0408 s.addAlgorithm(edm4hepReader)
0409
0410 s.addWhiteboardAlias(
0411 "particles", edm4hepReader.config.outputParticlesSimulation
0412 )
0413
0414 addSimParticleSelection(
0415 s,
0416 ParticleSelectorConfig(
0417 rho=(0.0 * u.mm, rhoMax),
0418 absZ=(0.0 * u.mm, 1.0 * u.m),
0419 eta=etaRange,
0420 removeNeutral=True,
0421 ),
0422 )
0423
0424 else:
0425
0426 if not args.ttbar_pu200:
0427 from acts.examples.simulation import addParticleGun
0428
0429 addParticleGun(
0430 s,
0431 MomentumConfig(
0432 *strToRange(args.gen_pt_range, "--gen-pt-range", u.GeV),
0433 transverse=True,
0434 ),
0435 EtaConfig(
0436 *(
0437 strToRange(args.gen_eta_range, "--gen-eta-range")
0438 if args.gen_eta_range
0439 else etaRange
0440 ),
0441 uniform=(
0442 not args.gen_cos_theta
0443 if args.gen_cos_theta or not args.odd
0444 else None
0445 ),
0446 ),
0447 PhiConfig(0.0, 360.0 * u.degree) if not args.itk else PhiConfig(),
0448 ParticleConfig(
0449 args.gen_nparticles, acts.PdgParticle.eMuon, randomizeCharge=True
0450 ),
0451 vtxGen=(
0452 acts.examples.GaussianVertexGenerator(
0453 mean=acts.Vector4(0, 0, 0, 0),
0454 stddev=acts.Vector4(
0455 0.0125 * u.mm, 0.0125 * u.mm, 55.5 * u.mm, 1.0 * u.ns
0456 ),
0457 )
0458 if args.odd
0459 else None
0460 ),
0461 multiplicity=args.gen_nvertices,
0462 rnd=rnd,
0463 outputDirRoot=outputDirMoreRoot,
0464 outputDirCsv=outputDirMoreCsv,
0465 )
0466 else:
0467 from acts.examples.simulation import addPythia8
0468
0469 addPythia8(
0470 s,
0471 hardProcess=["Top:qqbar2ttbar=on"],
0472 npileup=args.gen_nvertices,
0473 vtxGen=acts.examples.GaussianVertexGenerator(
0474 stddev=acts.Vector4(
0475 0.0125 * u.mm, 0.0125 * u.mm, 55.5 * u.mm, 5.0 * u.ns
0476 ),
0477 mean=acts.Vector4(0, 0, 0, 0),
0478 ),
0479 rnd=rnd,
0480 outputDirRoot=outputDirRoot,
0481 outputDirCsv=outputDirCsv,
0482 )
0483 addGenParticleSelection(
0484 s,
0485 ParticleSelectorConfig(
0486 rho=(0.0 * u.mm, rhoMax),
0487 absZ=(0.0 * u.mm, 1.0 * u.m),
0488 eta=etaRange,
0489 pt=(150 * u.MeV, None),
0490 ),
0491 )
0492
0493 if not args.geant4:
0494 from acts.examples.simulation import addFatras
0495
0496 addFatras(
0497 s,
0498 trackingGeometry,
0499 field,
0500 rnd=rnd,
0501 outputDirRoot=outputDirRoot,
0502 outputDirCsv=outputDirCsv,
0503 outputDirObj=outputDirObj,
0504 )
0505 else:
0506 if s.config.numThreads != 1:
0507 logger.fatal(
0508 f"Geant 4 simulation does not support multi-threading (threads={s.config.numThreads})"
0509 )
0510 sys.exit(2)
0511
0512 from acts.examples.simulation import addGeant4
0513
0514
0515
0516
0517 addGeant4(
0518 s,
0519 detector,
0520 trackingGeometry,
0521 field,
0522 rnd=rnd,
0523 killVolume=trackingGeometry.highestTrackingVolume,
0524 killAfterTime=25 * u.ns,
0525 outputDirRoot=outputDirRoot,
0526 outputDirCsv=outputDirCsv,
0527 outputDirObj=outputDirObj,
0528 )
0529
0530 addDigitization(
0531 s,
0532 trackingGeometry,
0533 field,
0534 digiConfigFile=args.digi_config,
0535 rnd=rnd,
0536 outputDirRoot=outputDirRoot,
0537 outputDirCsv=outputDirCsv,
0538 )
0539
0540 addDigiParticleSelection(
0541 s,
0542 ParticleSelectorConfig(
0543 pt=(ptMin, None),
0544 eta=etaRange if not args.generic_detector else (None, None),
0545 hits=(9, None),
0546 removeNeutral=True,
0547 ),
0548 )
0549
0550 if not args.reco:
0551 return s
0552
0553 from acts.examples.reconstruction import (
0554 addSeeding,
0555 TrackSmearingSigmas,
0556 addCKFTracks,
0557 CkfConfig,
0558 SeedingAlgorithm,
0559 TrackSelectorConfig,
0560 addAmbiguityResolution,
0561 AmbiguityResolutionConfig,
0562 addVertexFitting,
0563 VertexFinder,
0564 )
0565
0566 if args.itk and args.seeding_algorithm == SeedingAlgorithm.GridTriplet:
0567 seedingAlgConfig = itk.itkSeedingAlgConfig(
0568 itk.InputSpacePointsType.PixelSpacePoints
0569 )
0570 else:
0571 seedingAlgConfig = []
0572
0573 addSeeding(
0574 s,
0575 trackingGeometry,
0576 field,
0577 *seedingAlgConfig,
0578 seedingAlgorithm=args.seeding_algorithm,
0579 **(
0580 dict(
0581 trackSmearingSigmas=TrackSmearingSigmas(ptRel=0.01),
0582 rnd=rnd,
0583 )
0584 if args.seeding_algorithm == SeedingAlgorithm.TruthSmeared
0585 else {}
0586 ),
0587 initialSigmas=[
0588 1 * u.mm,
0589 1 * u.mm,
0590 1 * u.degree,
0591 1 * u.degree,
0592 0 * u.e / u.GeV,
0593 1 * u.ns,
0594 ],
0595 initialSigmaQoverPt=0.1 * u.e / u.GeV,
0596 initialSigmaPtRel=0.1,
0597 initialVarInflation=[1.0] * 6,
0598 geoSelectionConfigFile=seedingConfigFile,
0599 outputDirRoot=outputDirLessRoot,
0600 outputDirCsv=outputDirLessCsv,
0601 )
0602
0603 if args.MLSeedFilter:
0604 from acts.examples.reconstruction import (
0605 addSeedFilterML,
0606 SeedFilterMLDBScanConfig,
0607 )
0608
0609 addSeedFilterML(
0610 s,
0611 SeedFilterMLDBScanConfig(
0612 epsilonDBScan=0.03, minPointsDBScan=2, minSeedScore=0.1
0613 ),
0614 onnxModelFile=str(
0615 actsDir
0616 / "Examples/Scripts/Python/MLAmbiguityResolution/seedDuplicateClassifier.onnx"
0617 ),
0618 outputDirRoot=outputDirLessRoot,
0619 outputDirCsv=outputDirLessCsv,
0620 )
0621
0622 if not args.ckf:
0623 return s
0624
0625 if args.seeding_algorithm != SeedingAlgorithm.TruthSmeared:
0626 ckfConfig = CkfConfig(
0627 seedDeduplication=True,
0628 stayOnSeed=True,
0629 )
0630 else:
0631 ckfConfig = CkfConfig()
0632
0633 if not args.itk:
0634 trackSelectorConfig = TrackSelectorConfig(
0635 pt=(ptMin if args.ttbar_pu200 else 0.0, None),
0636 absEta=(None, 3.0),
0637 loc0=(-4.0 * u.mm, 4.0 * u.mm),
0638 nMeasurementsMin=7,
0639 maxHoles=2,
0640 maxOutliers=2,
0641 )
0642 ckfConfig = ckfConfig._replace(
0643 chi2CutOffMeasurement=15.0,
0644 chi2CutOffOutlier=25.0,
0645 numMeasurementsCutOff=2,
0646 )
0647 else:
0648
0649 trackSelectorConfig = (
0650 TrackSelectorConfig(absEta=(None, 2.0), pt=(0.9 * u.GeV, None), nMeasurementsMin=9, maxHoles=2, maxOutliers=2, maxSharedHits=2),
0651 TrackSelectorConfig(absEta=(None, 2.6), pt=(0.4 * u.GeV, None), nMeasurementsMin=8, maxHoles=2, maxOutliers=2, maxSharedHits=2),
0652 TrackSelectorConfig(absEta=(None, 4.0), pt=(0.4 * u.GeV, None), nMeasurementsMin=7, maxHoles=2, maxOutliers=2, maxSharedHits=2),
0653 )
0654
0655
0656 if args.odd:
0657 ckfConfig = ckfConfig._replace(
0658 pixelVolumes=[16, 17, 18],
0659 stripVolumes=[23, 24, 25],
0660 maxPixelHoles=1,
0661 maxStripHoles=2,
0662 constrainToVolumes=[
0663 2,
0664 32,
0665 4,
0666 16,
0667 17,
0668 18,
0669 20,
0670 23,
0671 24,
0672 25,
0673 26,
0674 8,
0675 28,
0676 29,
0677 30,
0678 ],
0679 )
0680 elif args.itk:
0681 ckfConfig = ckfConfig._replace(
0682
0683 pixelVolumes=[8, 9, 10, 13, 14, 15, 16, 18, 19, 20],
0684 stripVolumes=[22, 23, 24],
0685 maxPixelHoles=1,
0686 maxStripHoles=2,
0687 )
0688
0689 if args.output_detail == 1:
0690 writeDetail = dict(writeTrackSummary=False)
0691 elif args.output_detail == 2:
0692 writeDetail = dict(writeTrackStates=True)
0693 else:
0694 writeDetail = {}
0695
0696 if args.odd and args.output_detail != 1:
0697 writeCovMat = dict(writeCovMat=True)
0698 else:
0699 writeCovMat = {}
0700
0701 addCKFTracks(
0702 s,
0703 trackingGeometry,
0704 field,
0705 trackSelectorConfig=trackSelectorConfig,
0706 ckfConfig=ckfConfig,
0707 **writeDetail,
0708 **writeCovMat,
0709 outputDirRoot=outputDirLessRoot,
0710 outputDirCsv=outputDirLessCsv,
0711 )
0712
0713 if args.ambi_solver == "ML":
0714
0715 from acts.examples.reconstruction import (
0716 addAmbiguityResolutionML,
0717 AmbiguityResolutionMLConfig,
0718 )
0719
0720 addAmbiguityResolutionML(
0721 s,
0722 AmbiguityResolutionMLConfig(
0723 maximumSharedHits=3, maximumIterations=1000000, nMeasurementsMin=7
0724 ),
0725 onnxModelFile=str(
0726 actsDir
0727 / "Examples/Scripts/Python/MLAmbiguityResolution/duplicateClassifier.onnx"
0728 ),
0729 outputDirRoot=outputDirLessRoot,
0730 outputDirCsv=outputDirLessCsv,
0731 )
0732
0733 elif args.ambi_solver == "scoring":
0734
0735 from acts.examples.reconstruction import (
0736 addScoreBasedAmbiguityResolution,
0737 ScoreBasedAmbiguityResolutionConfig,
0738 )
0739
0740 addScoreBasedAmbiguityResolution(
0741 s,
0742 ScoreBasedAmbiguityResolutionConfig(
0743 minScore=0,
0744 minScoreSharedTracks=1,
0745 maxShared=2,
0746 minUnshared=3,
0747 maxSharedTracksPerMeasurement=2,
0748 useAmbiguityScoring=False,
0749 ),
0750 ambiVolumeFile=args.ambi_config,
0751 **writeCovMat,
0752 outputDirRoot=outputDirLessRoot,
0753 outputDirCsv=outputDirLessCsv,
0754 )
0755
0756 elif args.ambi_solver == "greedy":
0757
0758 addAmbiguityResolution(
0759 s,
0760 AmbiguityResolutionConfig(
0761 maximumSharedHits=3,
0762 maximumIterations=10000 if args.itk else 1000000,
0763 nMeasurementsMin=6 if args.itk else 7,
0764 ),
0765 **writeDetail,
0766 **writeCovMat,
0767 outputDirRoot=outputDirLessRoot,
0768 outputDirCsv=outputDirLessCsv,
0769 )
0770
0771 if args.vertexing:
0772 addVertexFitting(
0773 s,
0774 field,
0775 vertexFinder=VertexFinder.AMVF,
0776 outputDirRoot=outputDirLessRoot,
0777 outputDirCsv=outputDirLessCsv,
0778 )
0779
0780 return s
0781
0782
0783 def strToRange(s: str, optName: str, unit: float = 1.0):
0784 global logger
0785 try:
0786 range = [float(e) * unit if e != "" else None for e in s.split(":")]
0787 except ValueError:
0788 range = []
0789 if len(range) == 1:
0790 range.append(range[0])
0791 if len(range) != 2:
0792 logger.fatal(f"bad option value: {optName} {s}")
0793 sys.exit(2)
0794 return range
0795
0796
0797
0798 class EnumAction(argparse.Action):
0799 """
0800 Argparse action for handling Enums
0801 """
0802
0803 def __init__(self, **kwargs):
0804 import enum
0805
0806
0807 enum_type = kwargs.pop("enum", None)
0808
0809
0810 if enum_type is None:
0811 raise ValueError("type must be assigned an Enum when using EnumAction")
0812 if not issubclass(enum_type, enum.Enum):
0813 raise TypeError("type must be an Enum when using EnumAction")
0814
0815
0816 kwargs.setdefault("choices", tuple(e.name for e in enum_type))
0817
0818 super(EnumAction, self).__init__(**kwargs)
0819
0820 self._enum = enum_type
0821
0822 def __call__(self, parser, namespace, values, option_string=None):
0823 for e in self._enum:
0824 if e.name == values:
0825 setattr(namespace, self.dest, e)
0826 break
0827 else:
0828 raise ValueError("%s is not a validly enumerated algorithm." % values)
0829
0830
0831
0832 full_chain(parse_args()).run()