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