Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-15 09:25:58

0001 from pathlib import Path
0002 from typing import Optional, Union, List
0003 from enum import Enum
0004 from collections import namedtuple
0005 
0006 import acts
0007 import acts.examples
0008 
0009 u = acts.UnitConstants
0010 
0011 SeedingAlgorithm = Enum(
0012     "SeedingAlgorithm",
0013     "Default TruthSmeared TruthEstimated Orthogonal HoughTransform AdaptiveHoughTransform Gbts Hashing GridTriplet OrthogonalTriplet",
0014 )
0015 
0016 TrackSmearingSigmas = namedtuple(
0017     "TrackSmearingSigmas",
0018     [
0019         "loc0",
0020         "loc0PtA",
0021         "loc0PtB",
0022         "loc1",
0023         "loc1PtA",
0024         "loc1PtB",
0025         "time",
0026         "phi",
0027         "theta",
0028         "ptRel",
0029     ],
0030     defaults=[None] * 10,
0031 )
0032 
0033 SeedFinderConfigArg = namedtuple(
0034     "SeedFinderConfig",
0035     [
0036         "maxSeedsPerSpM",
0037         "cotThetaMax",
0038         "sigmaScattering",
0039         "radLengthPerSeed",
0040         "minPt",
0041         "impactMax",
0042         "deltaPhiMax",
0043         "interactionPointCut",
0044         "deltaZMax",
0045         "maxPtScattering",
0046         "zBinEdges",
0047         "zBinsCustomLooping",
0048         "rRangeMiddleSP",
0049         "useVariableMiddleSPRange",
0050         "binSizeR",
0051         "seedConfirmation",
0052         "centralSeedConfirmationRange",
0053         "forwardSeedConfirmationRange",
0054         "deltaR",  # (min,max)
0055         "deltaRBottomSP",  # (min,max)
0056         "deltaRTopSP",  # (min,max)
0057         "deltaRMiddleSPRange",  # (min,max)
0058         "collisionRegion",  # (min,max)
0059         "r",  # (min,max)
0060         "z",  # (min,max)
0061     ],
0062     defaults=[None] * 18 + [(None, None)] * 7,
0063 )
0064 SeedFinderOptionsArg = namedtuple(
0065     "SeedFinderOptions", ["beamPos", "bFieldInZ"], defaults=[(None, None), None]
0066 )
0067 
0068 SeedFilterConfigArg = namedtuple(
0069     "SeedFilterConfig",
0070     [
0071         "impactWeightFactor",
0072         "zOriginWeightFactor",
0073         "compatSeedWeight",
0074         "compatSeedLimit",
0075         "numSeedIncrement",
0076         "seedWeightIncrement",
0077         "seedConfirmation",
0078         "maxSeedsPerSpMConf",
0079         "maxQualitySeedsPerSpMConf",
0080         "useDeltaRorTopRadius",
0081         "deltaRMin",
0082     ],
0083     defaults=[None] * 11,
0084 )
0085 
0086 SpacePointGridConfigArg = namedtuple(
0087     "SeedGridConfig",
0088     [
0089         "rMax",
0090         "zBinEdges",
0091         "phiBinDeflectionCoverage",
0092         "impactMax",
0093         "deltaRMax",
0094         "maxPhiBins",
0095         "phi",  # (min,max)
0096     ],
0097     defaults=[None] * 6 + [(None, None)] * 1,
0098 )
0099 
0100 SeedingAlgorithmConfigArg = namedtuple(
0101     "SeedingAlgorithmConfig",
0102     [
0103         "allowSeparateRMax",
0104         "zBinNeighborsTop",
0105         "zBinNeighborsBottom",
0106         "numPhiNeighbors",
0107         "useExtraCuts",
0108     ],
0109     defaults=[None] * 5,
0110 )
0111 
0112 TruthEstimatedSeedingAlgorithmConfigArg = namedtuple(
0113     "TruthSeederConfig",
0114     [
0115         "deltaR",  # (min,max)
0116     ],
0117     defaults=[(None, None)],
0118 )
0119 
0120 TrackSelectorConfig = namedtuple(
0121     "TrackSelectorConfig",
0122     [
0123         "loc0",
0124         "loc1",
0125         "time",
0126         "eta",
0127         "absEta",
0128         "pt",
0129         "phi",
0130         "nMeasurementsMin",
0131         "maxHoles",
0132         "maxOutliers",
0133         "maxHolesAndOutliers",
0134         "maxSharedHits",
0135         "maxChi2",
0136         "nMeasurementsGroupMin",
0137         "requireReferenceSurface",
0138     ],
0139     defaults=[(None, None)] * 7 + [None] * 8,
0140 )
0141 
0142 
0143 def trackSelectorDefaultKWArgs(c):
0144     """
0145     Encapsulate this boilerplate code into a function so different uses do not get out of sync
0146     """
0147     return acts.examples.defaultKWArgs(
0148         loc0Min=c.loc0[0],
0149         loc0Max=c.loc0[1],
0150         loc1Min=c.loc1[0],
0151         loc1Max=c.loc1[1],
0152         timeMin=c.time[0],
0153         timeMax=c.time[1],
0154         phiMin=c.phi[0],
0155         phiMax=c.phi[1],
0156         etaMin=c.eta[0],
0157         etaMax=c.eta[1],
0158         absEtaMin=c.absEta[0],
0159         absEtaMax=c.absEta[1],
0160         ptMin=c.pt[0],
0161         ptMax=c.pt[1],
0162         minMeasurements=c.nMeasurementsMin,
0163         maxHoles=c.maxHoles,
0164         maxOutliers=c.maxOutliers,
0165         maxHolesAndOutliers=c.maxHolesAndOutliers,
0166         maxSharedHits=c.maxSharedHits,
0167         maxChi2=c.maxChi2,
0168         measurementCounter=c.nMeasurementsGroupMin,
0169         requireReferenceSurface=c.requireReferenceSurface,
0170     )
0171 
0172 
0173 CkfConfig = namedtuple(
0174     "CkfConfig",
0175     [
0176         "chi2CutOffMeasurement",
0177         "chi2CutOffOutlier",
0178         "numMeasurementsCutOff",
0179         "maxSteps",
0180         "seedDeduplication",
0181         "stayOnSeed",
0182         "pixelVolumes",
0183         "stripVolumes",
0184         "maxPixelHoles",
0185         "maxStripHoles",
0186         "trimTracks",
0187         "constrainToVolumes",
0188         "endOfWorldVolumes",
0189     ],
0190     defaults=[
0191         15.0,
0192         25.0,
0193         10,
0194         None,
0195         None,
0196         None,
0197         None,
0198         None,
0199         None,
0200         None,
0201         None,
0202         None,
0203         None,
0204     ],
0205 )
0206 
0207 AmbiguityResolutionConfig = namedtuple(
0208     "AmbiguityResolutionConfig",
0209     ["maximumSharedHits", "nMeasurementsMin", "maximumIterations"],
0210     defaults=[None] * 3,
0211 )
0212 
0213 ScoreBasedAmbiguityResolutionConfig = namedtuple(
0214     "ScoreBasedAmbiguityResolutionConfig",
0215     [
0216         "minScore",
0217         "minScoreSharedTracks",
0218         "maxShared",
0219         "minUnshared",
0220         "maxSharedTracksPerMeasurement",
0221         "useAmbiguityScoring",
0222     ],
0223     defaults=[None] * 6,
0224 )
0225 
0226 AmbiguityResolutionMLConfig = namedtuple(
0227     "AmbiguityResolutionMLConfig",
0228     ["maximumSharedHits", "nMeasurementsMin", "maximumIterations"],
0229     defaults=[None] * 3,
0230 )
0231 
0232 SeedFilterMLDBScanConfig = namedtuple(
0233     "SeedFilterMLDBScanConfig",
0234     ["epsilonDBScan", "minPointsDBScan", "minSeedScore"],
0235     defaults=[None] * 3,
0236 )
0237 
0238 HashingTrainingConfigArg = namedtuple(
0239     "HashingTrainingConfig",
0240     ["annoySeed", "f"],
0241     defaults=[None] * 2,
0242 )
0243 
0244 HashingAlgorithmConfigArg = namedtuple(
0245     "HashingAlgorithmConfig",
0246     ["bucketSize", "zBins", "phiBins"],
0247     defaults=[None] * 3,
0248 )
0249 
0250 
0251 class VertexFinder(Enum):
0252     Truth = (1,)
0253     AMVF = (2,)
0254     Iterative = (3,)
0255 
0256 
0257 @acts.examples.NamedTypeArgs(
0258     seedingAlgorithm=SeedingAlgorithm,
0259     trackSmearingSigmas=TrackSmearingSigmas,
0260     seedFinderConfigArg=SeedFinderConfigArg,
0261     seedFinderOptionsArg=SeedFinderOptionsArg,
0262     seedFilterConfigArg=SeedFilterConfigArg,
0263     spacePointGridConfigArg=SpacePointGridConfigArg,
0264     seedingAlgorithmConfigArg=SeedingAlgorithmConfigArg,
0265     hashingTrainingConfigArg=HashingTrainingConfigArg,
0266     hashingAlgorithmConfigArg=HashingAlgorithmConfigArg,
0267     truthEstimatedSeedingAlgorithmConfigArg=TruthEstimatedSeedingAlgorithmConfigArg,
0268     logLevel=acts.logging.Level,
0269 )
0270 def addSeeding(
0271     s: acts.examples.Sequencer,
0272     trackingGeometry: acts.TrackingGeometry,
0273     field: acts.MagneticFieldProvider,
0274     geoSelectionConfigFile: Optional[Union[Path, str]] = None,
0275     stripGeoSelectionConfigFile: Optional[Union[Path, str]] = None,
0276     layerMappingConfigFile: Optional[Union[Path, str]] = None,
0277     ConnectorInputConfigFile: Optional[Union[Path, str]] = None,
0278     seedingAlgorithm: SeedingAlgorithm = SeedingAlgorithm.GridTriplet,
0279     trackSmearingSigmas: TrackSmearingSigmas = TrackSmearingSigmas(),
0280     initialSigmas: Optional[list] = None,
0281     initialSigmaQoverPt: Optional[float] = None,
0282     initialSigmaPtRel: Optional[float] = None,
0283     initialVarInflation: Optional[list] = None,
0284     seedFinderConfigArg: SeedFinderConfigArg = SeedFinderConfigArg(),
0285     seedFinderOptionsArg: SeedFinderOptionsArg = SeedFinderOptionsArg(),
0286     seedFilterConfigArg: SeedFilterConfigArg = SeedFilterConfigArg(),
0287     spacePointGridConfigArg: SpacePointGridConfigArg = SpacePointGridConfigArg(),
0288     seedingAlgorithmConfigArg: SeedingAlgorithmConfigArg = SeedingAlgorithmConfigArg(),
0289     houghTransformConfig: acts.examples.HoughTransformSeeder.Config = acts.examples.HoughTransformSeeder.Config(),
0290     adaptiveHoughTransformConfig: Optional[
0291         acts.examples.AdaptiveHoughTransformSeeder.Config
0292     ] = None,
0293     hashingTrainingConfigArg: Optional[
0294         HashingTrainingConfigArg
0295     ] = HashingTrainingConfigArg(),
0296     hashingAlgorithmConfigArg: Optional[
0297         HashingAlgorithmConfigArg
0298     ] = HashingAlgorithmConfigArg(),
0299     truthEstimatedSeedingAlgorithmConfigArg: TruthEstimatedSeedingAlgorithmConfigArg = TruthEstimatedSeedingAlgorithmConfigArg(),
0300     particleHypothesis: Optional[
0301         acts.ParticleHypothesis
0302     ] = acts.ParticleHypothesis.pion,
0303     inputParticles: str = "particles",
0304     selectedParticles: str = "particles_selected",
0305     outputDirRoot: Optional[Union[Path, str]] = None,
0306     outputDirCsv: Optional[Union[Path, str]] = None,
0307     logLevel: Optional[acts.logging.Level] = None,
0308     rnd: Optional[acts.examples.RandomNumbers] = None,
0309 ) -> None:
0310     """This function steers the seeding
0311     Parameters
0312     ----------
0313     s: Sequencer
0314         the sequencer module to which we add the Seeding steps (returned from addSeeding)
0315     trackingGeometry : tracking geometry
0316     field : magnetic field
0317     geoSelectionConfigFile : Path|str, path, None
0318         Json file for space point geometry selection. Not required for SeedingAlgorithm.TruthSmeared.
0319     stripGeoSelectionConfigFile : Path|str, path, None
0320         Json file for space point geometry selection in strips. Needed for SpacePoint making.
0321     seedingAlgorithm : SeedingAlgorithm, Default
0322         seeding algorithm to use: one of Default (no truth information used), TruthSmeared, TruthEstimated
0323     trackSmearingSigmas : TrackSmearingSigmas(loc0, loc0PtA, loc0PtB, loc1, loc1PtA, loc1PtB, time, phi, theta, ptRel)
0324         TrackSmearing configuration.
0325         Defaults specified in Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TrackParameterSmearing.hpp
0326     initialSigmas : list
0327         Sets the initial covariance matrix diagonal. This is ignored in case of TruthSmearing.
0328         Defaults specified in Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackParamsEstimationAlgorithm.hpp
0329     initialVarInflation : list
0330         List of 6 scale factors to inflate the initial covariance matrix
0331         Defaults (all 1) specified in Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TrackParameterSmearing.hpp
0332     seedFinderConfigArg : SeedFinderConfigArg(maxSeedsPerSpM, cotThetaMax, sigmaScattering, radLengthPerSeed, minPt, impactMax, deltaPhiMax, interactionPointCut, deltaZMax, maxPtScattering, zBinEdges, zBinsCustomLooping, rRangeMiddleSP, useVariableMiddleSPRange, binSizeR, seedConfirmation, centralSeedConfirmationRange, forwardSeedConfirmationRange, deltaR, deltaRBottomSP, deltaRTopSP, deltaRMiddleSPRange, collisionRegion, r, z)
0333         SeedFinderConfig settings. deltaR, deltaRBottomSP, deltaRTopSP, deltaRMiddleSPRange, collisionRegion, r, z.
0334         Defaults specified in Core/include/Acts/Seeding/SeedFinderConfig.hpp
0335     seedFinderOptionsArg :  SeedFinderOptionsArg(bFieldInZ, beamPos)
0336         Defaults specified in Core/include/Acts/Seeding/SeedFinderConfig.hpp
0337     seedFilterConfigArg : SeedFilterConfigArg(compatSeedWeight, compatSeedLimit, numSeedIncrement, seedWeightIncrement, seedConfirmation, maxSeedsPerSpMConf, maxQualitySeedsPerSpMConf, useDeltaRorTopRadius)
0338                                 Defaults specified in Core/include/Acts/Seeding/SeedFilterConfig.hpp
0339     spacePointGridConfigArg : SpacePointGridConfigArg(rMax, zBinEdges, phiBinDeflectionCoverage, phi, maxPhiBins, impactMax)
0340                                 SpacePointGridConfigArg settings. phi is specified as a tuple of (min,max).
0341         Defaults specified in Core/include/Acts/Seeding/SpacePointGrid.hpp
0342     seedingAlgorithmConfigArg : SeedingAlgorithmConfigArg(allowSeparateRMax, zBinNeighborsTop, zBinNeighborsBottom, numPhiNeighbors, useExtraCuts)
0343                                 Defaults specified in Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/SeedingAlgorithm.hpp
0344     hashingTrainingConfigArg : HashingTrainingConfigArg(annoySeed, f)
0345                                 Defaults specified in Plugins/Hashing/include/ActsPlugins/Hashing/HashingTrainingConfig.hpp
0346     hashingAlgorithmConfigArg : HashingAlgorithmConfigArg(bucketSize, zBins, phiBins)
0347                                 Defaults specified in Plugins/Hashing/include/ActsPlugins/Hashing/HashingAlgorithmConfig.hpp
0348     truthEstimatedSeedingAlgorithmConfigArg : TruthEstimatedSeedingAlgorithmConfigArg(deltaR)
0349         Currently only deltaR=(min,max) range specified here.
0350     particleHypothesis : Optional[acts.ParticleHypothesis]
0351         The hypothesis used for track finding. Defaults to pion.
0352     inputParticles : str, "particles"
0353         input particles name in the WhiteBoard
0354     selectedParticles : str, "particles_selected"
0355         selected particles name in the WhiteBoard
0356     outputDirRoot : Path|str, path, None
0357         the output folder for ROOT output, None triggers no output
0358     logLevel : acts.logging.Level, None
0359         logging level to override setting given in `s`
0360     rnd : RandomNumbers, None
0361         random number generator. Only used by SeedingAlgorithm.TruthSmeared.
0362     """
0363 
0364     logLevel = acts.examples.defaultLogging(s, logLevel)()
0365     logger = acts.logging.getLogger("addSeeding")
0366     logger.setLevel(logLevel)
0367 
0368     # Create starting parameters from either particle smearing or combined seed
0369     # finding and track parameters estimation
0370     if seedingAlgorithm == SeedingAlgorithm.TruthSmeared:
0371         logger.info("Using smeared truth particles for seeding")
0372         addTruthSmearedSeeding(
0373             s=s,
0374             rnd=rnd,
0375             selectedParticles=selectedParticles,
0376             trackSmearingSigmas=trackSmearingSigmas,
0377             initialSigmas=initialSigmas,
0378             initialSigmaQoverPt=initialSigmaQoverPt,
0379             initialSigmaPtRel=initialSigmaPtRel,
0380             initialVarInflation=initialVarInflation,
0381             particleHypothesis=particleHypothesis,
0382             logLevel=logLevel,
0383         )
0384     else:
0385         spacePoints = addSpacePointsMaking(
0386             s,
0387             trackingGeometry,
0388             geoSelectionConfigFile,
0389             stripGeoSelectionConfigFile,
0390             logLevel,
0391         )
0392         seeds = None
0393         perSeedParticleHypothesis = None
0394         # Run either: truth track finding or seeding
0395         if seedingAlgorithm == SeedingAlgorithm.TruthEstimated:
0396             logger.info("Using truth track finding from space points for seeding")
0397             seeds, perSeedParticleHypothesis = addTruthEstimatedSeeding(
0398                 s,
0399                 spacePoints,
0400                 selectedParticles,
0401                 truthEstimatedSeedingAlgorithmConfigArg,
0402                 particleHypothesis=particleHypothesis,
0403                 logLevel=logLevel,
0404             )
0405         elif seedingAlgorithm == SeedingAlgorithm.Default:
0406             logger.info("Using default seeding")
0407             seeds = addStandardSeeding(
0408                 s,
0409                 spacePoints,
0410                 seedingAlgorithmConfigArg,
0411                 seedFinderConfigArg,
0412                 seedFinderOptionsArg,
0413                 seedFilterConfigArg,
0414                 spacePointGridConfigArg,
0415                 logLevel,
0416             )
0417         elif seedingAlgorithm == SeedingAlgorithm.Orthogonal:
0418             logger.info("Using orthogonal seeding")
0419             seeds = addOrthogonalSeeding(
0420                 s,
0421                 spacePoints,
0422                 seedFinderConfigArg,
0423                 seedFinderOptionsArg,
0424                 seedFilterConfigArg,
0425                 logLevel,
0426             )
0427         elif seedingAlgorithm == SeedingAlgorithm.HoughTransform:
0428             logger.info("Using Hough Transform seeding")
0429             houghTransformConfig.inputSpacePoints = [spacePoints]
0430             houghTransformConfig.inputMeasurements = "measurements"
0431             houghTransformConfig.outputProtoTracks = "prototracks"
0432             houghTransformConfig.outputSeeds = "seeds"
0433             houghTransformConfig.trackingGeometry = trackingGeometry
0434             seeds = addHoughTransformSeeding(s, houghTransformConfig, logLevel)
0435         elif seedingAlgorithm == SeedingAlgorithm.AdaptiveHoughTransform:
0436             logger.info("Using Adaptive Hough Transform seeding")
0437             adaptiveHoughTransformConfig.inputSpacePoints = [spacePoints]
0438             adaptiveHoughTransformConfig.outputProtoTracks = "prototracks"
0439             adaptiveHoughTransformConfig.outputSeeds = "seeds"
0440             adaptiveHoughTransformConfig.trackingGeometry = trackingGeometry
0441             adaptiveHoughTransformConfig.threshold = 4
0442             adaptiveHoughTransformConfig.noiseThreshold = 12
0443             adaptiveHoughTransformConfig.phiMinBinSize = 3.14 / (2.0 * 257.0)
0444             adaptiveHoughTransformConfig.qOverPtMinBinSize = 1.1 / (2.0 * 257.0)
0445             adaptiveHoughTransformConfig.qOverPtMin = 1.1
0446             adaptiveHoughTransformConfig.doSecondPhase = True
0447             adaptiveHoughTransformConfig.zMinBinSize = 1 * u.mm
0448             adaptiveHoughTransformConfig.cotThetaMinBinSize = 0.1
0449             adaptiveHoughTransformConfig.deduplicate = True
0450             seeds = addAdaptiveHoughTransformSeeding(
0451                 s, adaptiveHoughTransformConfig, logLevel=logLevel
0452             )
0453         elif seedingAlgorithm == SeedingAlgorithm.Gbts:
0454             logger.info("Using Gbts seeding")
0455             # output of algs changed, only one output now
0456             seeds = addGbtsSeeding(
0457                 s,
0458                 spacePoints,
0459                 seedFinderConfigArg,
0460                 seedFinderOptionsArg,
0461                 trackingGeometry,
0462                 logLevel,
0463                 layerMappingConfigFile,
0464                 geoSelectionConfigFile,
0465                 ConnectorInputConfigFile,
0466             )
0467         elif seedingAlgorithm == SeedingAlgorithm.Hashing:
0468             logger.info("Using Hashing seeding")
0469             seeds, buckets = addHashingSeeding(
0470                 s,
0471                 spacePoints,
0472                 seedingAlgorithmConfigArg,
0473                 seedFinderConfigArg,
0474                 seedFinderOptionsArg,
0475                 seedFilterConfigArg,
0476                 spacePointGridConfigArg,
0477                 hashingTrainingConfigArg,
0478                 hashingAlgorithmConfigArg,
0479                 logLevel,
0480             )
0481         elif seedingAlgorithm == SeedingAlgorithm.GridTriplet:
0482             logger.info("Using grid triplet seeding")
0483             seeds = addGridTripletSeeding(
0484                 s,
0485                 spacePoints,
0486                 seedingAlgorithmConfigArg,
0487                 seedFinderConfigArg,
0488                 seedFinderOptionsArg,
0489                 seedFilterConfigArg,
0490                 spacePointGridConfigArg,
0491                 logLevel,
0492             )
0493         elif seedingAlgorithm == SeedingAlgorithm.OrthogonalTriplet:
0494             logger.info("Using orthogonal triplet seeding")
0495             seeds = addOrthogonalTripletSeeding(
0496                 s,
0497                 spacePoints,
0498                 seedingAlgorithmConfigArg,
0499                 seedFinderConfigArg,
0500                 seedFinderOptionsArg,
0501                 seedFilterConfigArg,
0502                 spacePointGridConfigArg,
0503                 logLevel,
0504             )
0505         else:
0506             logger.fatal("unknown seedingAlgorithm %s", seedingAlgorithm)
0507 
0508         parEstimateAlg = acts.examples.TrackParamsEstimationAlgorithm(
0509             level=logLevel,
0510             inputSeeds=seeds,
0511             inputParticleHypotheses=perSeedParticleHypothesis,
0512             outputTrackParameters="estimatedparameters",
0513             outputSeeds="estimatedseeds",
0514             trackingGeometry=trackingGeometry,
0515             magneticField=field,
0516             **acts.examples.defaultKWArgs(
0517                 initialSigmas=initialSigmas,
0518                 initialSigmaQoverPt=initialSigmaQoverPt,
0519                 initialSigmaPtRel=initialSigmaPtRel,
0520                 initialVarInflation=initialVarInflation,
0521                 particleHypothesis=particleHypothesis,
0522             ),
0523         )
0524         s.addAlgorithm(parEstimateAlg)
0525 
0526         prototracks = "seed-prototracks"
0527         s.addAlgorithm(
0528             acts.examples.SeedsToPrototracks(
0529                 level=logLevel,
0530                 inputSeeds="estimatedseeds",
0531                 outputProtoTracks=prototracks,
0532             )
0533         )
0534 
0535         tracks = "seed-tracks"
0536         s.addAlgorithm(
0537             acts.examples.PrototracksToTracks(
0538                 level=logLevel,
0539                 inputProtoTracks=prototracks,
0540                 inputTrackParameters="estimatedparameters",
0541                 inputMeasurements="measurements",
0542                 outputTracks=tracks,
0543             )
0544         )
0545 
0546         s.addAlgorithm(
0547             acts.examples.TrackTruthMatcher(
0548                 level=logLevel,
0549                 inputTracks=tracks,
0550                 inputParticles=selectedParticles,
0551                 inputMeasurementParticlesMap="measurement_particles_map",
0552                 outputTrackParticleMatching="seed_particle_matching",
0553                 outputParticleTrackMatching="particle_seed_matching",
0554                 matchingRatio=1.0,
0555                 doubleMatching=False,
0556             )
0557         )
0558 
0559         if outputDirRoot is not None:
0560             addSeedPerformanceWriters(
0561                 s,
0562                 outputDirRoot,
0563                 tracks,
0564                 prototracks,
0565                 selectedParticles,
0566                 inputParticles,
0567                 parEstimateAlg.config.outputTrackParameters,
0568                 logLevel,
0569             )
0570 
0571         if outputDirCsv is not None:
0572             outputDirCsv = Path(outputDirCsv)
0573 
0574             if not outputDirCsv.exists():
0575                 outputDirCsv.mkdir()
0576 
0577             csvSeedWriter = acts.examples.CsvSeedWriter(
0578                 level=logLevel,
0579                 inputTrackParameters=parEstimateAlg.config.outputTrackParameters,
0580                 inputSimSeeds=seeds,
0581                 inputSimHits="simhits",
0582                 inputMeasurementParticlesMap="measurement_particles_map",
0583                 inputMeasurementSimHitsMap="measurement_simhits_map",
0584                 outputDir=str(outputDirCsv),
0585                 fileName=str(f"seed.csv"),
0586             )
0587             s.addWriter(csvSeedWriter)
0588 
0589             if seedingAlgorithm == SeedingAlgorithm.Hashing:
0590                 s.addWriter(
0591                     acts.examples.CsvSpacePointsBucketWriter(
0592                         level=logLevel,
0593                         inputBuckets=buckets,
0594                         outputDir=str(outputDirCsv),
0595                     )
0596                 )
0597 
0598     return s
0599 
0600 
0601 def addTruthSmearedSeeding(
0602     s: acts.examples.Sequencer,
0603     rnd: Optional[acts.examples.RandomNumbers],
0604     selectedParticles: str,
0605     trackSmearingSigmas: TrackSmearingSigmas,
0606     initialSigmas: Optional[List[float]],
0607     initialSigmaQoverPt: Optional[float],
0608     initialSigmaPtRel: Optional[float],
0609     initialVarInflation: Optional[List[float]],
0610     particleHypothesis: Optional[acts.ParticleHypothesis],
0611     logLevel: acts.logging.Level = None,
0612 ):
0613     """adds algorithm that would mimic detector response uncertainties for truth seeding
0614     For parameters description see addSeeding
0615     """
0616 
0617     rnd = rnd or acts.examples.RandomNumbers(seed=42)
0618 
0619     trkParamExtractor = acts.examples.ParticleTrackParamExtractor(
0620         level=logLevel,
0621         inputParticles=selectedParticles,
0622         outputTrackParameters="trueparameters",
0623     )
0624     s.addAlgorithm(trkParamExtractor)
0625 
0626     # Smearing track parameters
0627     trkSmear = acts.examples.TrackParameterSmearing(
0628         level=logLevel,
0629         inputTrackParameters=trkParamExtractor.config.outputTrackParameters,
0630         outputTrackParameters="estimatedparameters",
0631         randomNumbers=rnd,
0632         # gaussian sigmas to smear particle parameters
0633         **acts.examples.defaultKWArgs(
0634             sigmaLoc0=trackSmearingSigmas.loc0,
0635             sigmaLoc0PtA=trackSmearingSigmas.loc0PtA,
0636             sigmaLoc0PtB=trackSmearingSigmas.loc0PtB,
0637             sigmaLoc1=trackSmearingSigmas.loc1,
0638             sigmaLoc1PtA=trackSmearingSigmas.loc1PtA,
0639             sigmaLoc1PtB=trackSmearingSigmas.loc1PtB,
0640             sigmaTime=trackSmearingSigmas.time,
0641             sigmaPhi=trackSmearingSigmas.phi,
0642             sigmaTheta=trackSmearingSigmas.theta,
0643             sigmaPtRel=trackSmearingSigmas.ptRel,
0644             initialSigmas=initialSigmas,
0645             initialSigmaQoverPt=initialSigmaQoverPt,
0646             initialSigmaPtRel=initialSigmaPtRel,
0647             initialVarInflation=initialVarInflation,
0648             particleHypothesis=particleHypothesis,
0649         ),
0650     )
0651     s.addAlgorithm(trkSmear)
0652 
0653     truthTrkFndAlg = acts.examples.TruthTrackFinder(
0654         level=logLevel,
0655         inputParticles=selectedParticles,
0656         inputMeasurements="measurements",
0657         inputParticleMeasurementsMap="particle_measurements_map",
0658         inputSimHits="simhits",
0659         inputMeasurementSimHitsMap="measurement_simhits_map",
0660         outputProtoTracks="truth_particle_tracks",
0661     )
0662     s.addAlgorithm(truthTrkFndAlg)
0663 
0664 
0665 def addTruthEstimatedSeeding(
0666     sequence: acts.examples.Sequencer,
0667     spacePoints: str,
0668     inputParticles: str,
0669     TruthEstimatedSeedingAlgorithmConfigArg: TruthEstimatedSeedingAlgorithmConfigArg,
0670     particleHypothesis: Optional[acts.ParticleHypothesis] = None,
0671     logLevel: acts.logging.Level = None,
0672 ):
0673     """adds truth seeding
0674     For parameters description see addSeeding
0675     """
0676     logLevel = acts.examples.defaultLogging(sequence, logLevel)()
0677 
0678     truthSeeding = acts.examples.TruthSeedingAlgorithm(
0679         level=logLevel,
0680         inputParticles=inputParticles,
0681         inputParticleMeasurementsMap="particle_measurements_map",
0682         inputSpacePoints=[spacePoints],
0683         inputSimHits="simhits",
0684         inputMeasurementSimHitsMap="measurement_simhits_map",
0685         outputParticles="truth_seeded_particles",
0686         outputProtoTracks="truth_particle_tracks",
0687         outputSeeds="seeds",
0688         outputParticleHypotheses="seed_particle_hypotheses",
0689         **acts.examples.defaultKWArgs(
0690             deltaRMin=TruthEstimatedSeedingAlgorithmConfigArg.deltaR[0],
0691             deltaRMax=TruthEstimatedSeedingAlgorithmConfigArg.deltaR[1],
0692             particleHypothesis=particleHypothesis,
0693         ),
0694     )
0695     sequence.addAlgorithm(truthSeeding)
0696 
0697     return truthSeeding.config.outputSeeds, truthSeeding.config.outputParticleHypotheses
0698 
0699 
0700 def addSpacePointsMaking(
0701     sequence: acts.examples.Sequencer,
0702     trackingGeometry: acts.TrackingGeometry,
0703     geoSelectionConfigFile: Union[Path, str],
0704     stripGeoSelectionConfigFile: Union[Path, str],
0705     logLevel: acts.logging.Level = None,
0706 ):
0707     """adds space points making
0708     For parameters description see addSeeding
0709     """
0710     logLevel = acts.examples.defaultLogging(sequence, logLevel)()
0711     spAlg = acts.examples.SpacePointMaker(
0712         level=logLevel,
0713         inputMeasurements="measurements",
0714         outputSpacePoints="spacepoints",
0715         trackingGeometry=trackingGeometry,
0716         geometrySelection=acts.examples.json.readJsonGeometryList(
0717             str(geoSelectionConfigFile)
0718         ),
0719         stripGeometrySelection=(
0720             acts.examples.json.readJsonGeometryList(str(stripGeoSelectionConfigFile))
0721             if stripGeoSelectionConfigFile
0722             else []
0723         ),
0724     )
0725     sequence.addAlgorithm(spAlg)
0726     return spAlg.config.outputSpacePoints
0727 
0728 
0729 def addStandardSeeding(
0730     sequence: acts.examples.Sequencer,
0731     spacePoints: str,
0732     seedingAlgorithmConfigArg: SeedingAlgorithmConfigArg,
0733     seedFinderConfigArg: SeedFinderConfigArg,
0734     seedFinderOptionsArg: SeedFinderOptionsArg,
0735     seedFilterConfigArg: SeedFilterConfigArg,
0736     spacePointGridConfigArg: SpacePointGridConfigArg,
0737     logLevel: acts.logging.Level = None,
0738     outputSeeds: str = "seeds",
0739 ):
0740     """adds standard seeding
0741     For parameters description see addSeeding
0742     """
0743     logLevel = acts.examples.defaultLogging(sequence, logLevel)()
0744 
0745     seedFinderConfig = acts.examples.SeedFinderConfig(
0746         **acts.examples.defaultKWArgs(
0747             rMin=seedFinderConfigArg.r[0],
0748             rMax=seedFinderConfigArg.r[1],
0749             deltaRMin=seedFinderConfigArg.deltaR[0],
0750             deltaRMax=seedFinderConfigArg.deltaR[1],
0751             deltaRMinTopSP=(
0752                 seedFinderConfigArg.deltaR[0]
0753                 if seedFinderConfigArg.deltaRTopSP[0] is None
0754                 else seedFinderConfigArg.deltaRTopSP[0]
0755             ),
0756             deltaRMaxTopSP=(
0757                 seedFinderConfigArg.deltaR[1]
0758                 if seedFinderConfigArg.deltaRTopSP[1] is None
0759                 else seedFinderConfigArg.deltaRTopSP[1]
0760             ),
0761             deltaRMinBottomSP=(
0762                 seedFinderConfigArg.deltaR[0]
0763                 if seedFinderConfigArg.deltaRBottomSP[0] is None
0764                 else seedFinderConfigArg.deltaRBottomSP[0]
0765             ),
0766             deltaRMaxBottomSP=(
0767                 seedFinderConfigArg.deltaR[1]
0768                 if seedFinderConfigArg.deltaRBottomSP[1] is None
0769                 else seedFinderConfigArg.deltaRBottomSP[1]
0770             ),
0771             deltaRMiddleMinSPRange=seedFinderConfigArg.deltaRMiddleSPRange[0],
0772             deltaRMiddleMaxSPRange=seedFinderConfigArg.deltaRMiddleSPRange[1],
0773             collisionRegionMin=seedFinderConfigArg.collisionRegion[0],
0774             collisionRegionMax=seedFinderConfigArg.collisionRegion[1],
0775             zMin=seedFinderConfigArg.z[0],
0776             zMax=seedFinderConfigArg.z[1],
0777             maxSeedsPerSpM=seedFinderConfigArg.maxSeedsPerSpM,
0778             cotThetaMax=seedFinderConfigArg.cotThetaMax,
0779             sigmaScattering=seedFinderConfigArg.sigmaScattering,
0780             radLengthPerSeed=seedFinderConfigArg.radLengthPerSeed,
0781             minPt=seedFinderConfigArg.minPt,
0782             impactMax=seedFinderConfigArg.impactMax,
0783             interactionPointCut=seedFinderConfigArg.interactionPointCut,
0784             deltaZMax=seedFinderConfigArg.deltaZMax,
0785             maxPtScattering=seedFinderConfigArg.maxPtScattering,
0786             zBinEdges=seedFinderConfigArg.zBinEdges,
0787             zBinsCustomLooping=seedFinderConfigArg.zBinsCustomLooping,
0788             rRangeMiddleSP=seedFinderConfigArg.rRangeMiddleSP,
0789             useVariableMiddleSPRange=seedFinderConfigArg.useVariableMiddleSPRange,
0790             binSizeR=seedFinderConfigArg.binSizeR,
0791             seedConfirmation=seedFinderConfigArg.seedConfirmation,
0792             centralSeedConfirmationRange=seedFinderConfigArg.centralSeedConfirmationRange,
0793             forwardSeedConfirmationRange=seedFinderConfigArg.forwardSeedConfirmationRange,
0794         ),
0795     )
0796     seedFinderOptions = acts.SeedFinderOptions(
0797         **acts.examples.defaultKWArgs(
0798             beamPos=(
0799                 acts.Vector2(0.0, 0.0)
0800                 if seedFinderOptionsArg.beamPos == (None, None)
0801                 else acts.Vector2(
0802                     seedFinderOptionsArg.beamPos[0], seedFinderOptionsArg.beamPos[1]
0803                 )
0804             ),
0805             bFieldInZ=seedFinderOptionsArg.bFieldInZ,
0806         )
0807     )
0808     seedFilterConfig = acts.SeedFilterConfig(
0809         **acts.examples.defaultKWArgs(
0810             maxSeedsPerSpM=seedFinderConfig.maxSeedsPerSpM,
0811             deltaRMin=(
0812                 seedFinderConfig.deltaRMin
0813                 if seedFilterConfigArg.deltaRMin is None
0814                 else seedFilterConfigArg.deltaRMin
0815             ),
0816             impactWeightFactor=seedFilterConfigArg.impactWeightFactor,
0817             zOriginWeightFactor=seedFilterConfigArg.zOriginWeightFactor,
0818             compatSeedWeight=seedFilterConfigArg.compatSeedWeight,
0819             compatSeedLimit=seedFilterConfigArg.compatSeedLimit,
0820             numSeedIncrement=seedFilterConfigArg.numSeedIncrement,
0821             seedWeightIncrement=seedFilterConfigArg.seedWeightIncrement,
0822             seedConfirmation=seedFilterConfigArg.seedConfirmation,
0823             centralSeedConfirmationRange=seedFinderConfig.centralSeedConfirmationRange,
0824             forwardSeedConfirmationRange=seedFinderConfig.forwardSeedConfirmationRange,
0825             maxSeedsPerSpMConf=seedFilterConfigArg.maxSeedsPerSpMConf,
0826             maxQualitySeedsPerSpMConf=seedFilterConfigArg.maxQualitySeedsPerSpMConf,
0827             useDeltaRorTopRadius=seedFilterConfigArg.useDeltaRorTopRadius,
0828         )
0829     )
0830 
0831     gridConfig = acts.SpacePointGridConfig(
0832         **acts.examples.defaultKWArgs(
0833             minPt=seedFinderConfig.minPt,
0834             rMax=(
0835                 seedFinderConfig.rMax
0836                 if spacePointGridConfigArg.rMax is None
0837                 else spacePointGridConfigArg.rMax
0838             ),
0839             zMax=seedFinderConfig.zMax,
0840             zMin=seedFinderConfig.zMin,
0841             deltaRMax=(
0842                 seedFinderConfig.deltaRMax
0843                 if spacePointGridConfigArg.deltaRMax is None
0844                 else spacePointGridConfigArg.deltaRMax
0845             ),
0846             cotThetaMax=seedFinderConfig.cotThetaMax,
0847             phiMin=spacePointGridConfigArg.phi[0],
0848             phiMax=spacePointGridConfigArg.phi[1],
0849             maxPhiBins=spacePointGridConfigArg.maxPhiBins,
0850             impactMax=spacePointGridConfigArg.impactMax,
0851             zBinEdges=spacePointGridConfigArg.zBinEdges,
0852             phiBinDeflectionCoverage=spacePointGridConfigArg.phiBinDeflectionCoverage,
0853         )
0854     )
0855 
0856     gridOptions = acts.SpacePointGridOptions(
0857         **acts.examples.defaultKWArgs(
0858             bFieldInZ=seedFinderOptions.bFieldInZ,
0859         )
0860     )
0861 
0862     seedingAlg = acts.examples.SeedingAlgorithm(
0863         level=logLevel,
0864         inputSpacePoints=[spacePoints],
0865         outputSeeds=outputSeeds,
0866         **acts.examples.defaultKWArgs(
0867             allowSeparateRMax=seedingAlgorithmConfigArg.allowSeparateRMax,
0868             zBinNeighborsTop=seedingAlgorithmConfigArg.zBinNeighborsTop,
0869             zBinNeighborsBottom=seedingAlgorithmConfigArg.zBinNeighborsBottom,
0870             numPhiNeighbors=seedingAlgorithmConfigArg.numPhiNeighbors,
0871             useExtraCuts=seedingAlgorithmConfigArg.useExtraCuts,
0872         ),
0873         gridConfig=gridConfig,
0874         gridOptions=gridOptions,
0875         seedFilterConfig=seedFilterConfig,
0876         seedFinderConfig=seedFinderConfig,
0877         seedFinderOptions=seedFinderOptions,
0878     )
0879     sequence.addAlgorithm(seedingAlg)
0880 
0881     return seedingAlg.config.outputSeeds
0882 
0883 
0884 def addGridTripletSeeding(
0885     sequence: acts.examples.Sequencer,
0886     spacePoints: str,
0887     seedingAlgorithmConfigArg: SeedingAlgorithmConfigArg,
0888     seedFinderConfigArg: SeedFinderConfigArg,
0889     seedFinderOptionsArg: SeedFinderOptionsArg,
0890     seedFilterConfigArg: SeedFilterConfigArg,
0891     spacePointGridConfigArg: SpacePointGridConfigArg,
0892     logLevel: acts.logging.Level = None,
0893     outputSeeds: str = "seeds",
0894 ):
0895     """adds grid triplet seeding
0896     For parameters description see addSeeding
0897     """
0898     logLevel = acts.examples.defaultLogging(sequence, logLevel)()
0899 
0900     seedingAlg = acts.examples.GridTripletSeedingAlgorithm(
0901         level=logLevel,
0902         inputSpacePoints=spacePoints,
0903         outputSeeds=outputSeeds,
0904         **acts.examples.defaultKWArgs(
0905             bFieldInZ=seedFinderOptionsArg.bFieldInZ,
0906             minPt=seedFinderConfigArg.minPt,
0907             cotThetaMax=seedFinderConfigArg.cotThetaMax,
0908             impactMax=seedFinderConfigArg.impactMax,
0909             deltaRMin=seedFinderConfigArg.deltaR[0],
0910             deltaRMax=seedFinderConfigArg.deltaR[1],
0911             deltaRMinTop=(
0912                 seedFinderConfigArg.deltaR[0]
0913                 if seedFinderConfigArg.deltaRTopSP[0] is None
0914                 else seedFinderConfigArg.deltaRTopSP[0]
0915             ),
0916             deltaRMaxTop=(
0917                 seedFinderConfigArg.deltaR[1]
0918                 if seedFinderConfigArg.deltaRTopSP[1] is None
0919                 else seedFinderConfigArg.deltaRTopSP[1]
0920             ),
0921             deltaRMinBottom=(
0922                 seedFinderConfigArg.deltaR[0]
0923                 if seedFinderConfigArg.deltaRBottomSP[0] is None
0924                 else seedFinderConfigArg.deltaRBottomSP[0]
0925             ),
0926             deltaRMaxBottom=(
0927                 seedFinderConfigArg.deltaR[1]
0928                 if seedFinderConfigArg.deltaRBottomSP[1] is None
0929                 else seedFinderConfigArg.deltaRBottomSP[1]
0930             ),
0931             rMin=seedFinderConfigArg.r[0],
0932             rMax=seedFinderConfigArg.r[1],
0933             zMin=seedFinderConfigArg.z[0],
0934             zMax=seedFinderConfigArg.z[1],
0935             phiMin=spacePointGridConfigArg.phi[0],
0936             phiMax=spacePointGridConfigArg.phi[1],
0937             phiBinDeflectionCoverage=spacePointGridConfigArg.phiBinDeflectionCoverage,
0938             maxPhiBins=spacePointGridConfigArg.maxPhiBins,
0939             zBinEdges=spacePointGridConfigArg.zBinEdges,
0940             zBinsCustomLooping=seedFinderConfigArg.zBinsCustomLooping,
0941             rMinMiddle=None,
0942             rMaxMiddle=None,
0943             useVariableMiddleSPRange=seedFinderConfigArg.useVariableMiddleSPRange,
0944             rRangeMiddleSP=seedFinderConfigArg.rRangeMiddleSP,
0945             deltaRMiddleMinSPRange=seedFinderConfigArg.deltaRMiddleSPRange[0],
0946             deltaRMiddleMaxSPRange=seedFinderConfigArg.deltaRMiddleSPRange[1],
0947             deltaZMin=None,
0948             deltaZMax=None,
0949             interactionPointCut=seedFinderConfigArg.interactionPointCut,
0950             collisionRegionMin=seedFinderConfigArg.collisionRegion[0],
0951             collisionRegionMax=seedFinderConfigArg.collisionRegion[1],
0952             helixCutTolerance=None,
0953             sigmaScattering=seedFinderConfigArg.sigmaScattering,
0954             radLengthPerSeed=seedFinderConfigArg.radLengthPerSeed,
0955             toleranceParam=None,
0956             deltaInvHelixDiameter=None,
0957             compatSeedWeight=seedFilterConfigArg.compatSeedWeight,
0958             impactWeightFactor=seedFilterConfigArg.impactWeightFactor,
0959             zOriginWeightFactor=seedFilterConfigArg.zOriginWeightFactor,
0960             maxSeedsPerSpM=seedFinderConfigArg.maxSeedsPerSpM,
0961             compatSeedLimit=seedFilterConfigArg.compatSeedLimit,
0962             seedWeightIncrement=seedFilterConfigArg.seedWeightIncrement,
0963             numSeedIncrement=seedFilterConfigArg.numSeedIncrement,
0964             seedConfirmation=seedFinderConfigArg.seedConfirmation,
0965             centralSeedConfirmationRange=seedFinderConfigArg.centralSeedConfirmationRange,
0966             forwardSeedConfirmationRange=seedFinderConfigArg.forwardSeedConfirmationRange,
0967             maxSeedsPerSpMConf=seedFilterConfigArg.maxSeedsPerSpMConf,
0968             maxQualitySeedsPerSpMConf=seedFilterConfigArg.maxQualitySeedsPerSpMConf,
0969             useDeltaRinsteadOfTopRadius=seedFilterConfigArg.useDeltaRorTopRadius,
0970             useExtraCuts=seedingAlgorithmConfigArg.useExtraCuts,
0971         ),
0972     )
0973     sequence.addAlgorithm(seedingAlg)
0974 
0975     return seedingAlg.config.outputSeeds
0976 
0977 
0978 def addOrthogonalTripletSeeding(
0979     sequence: acts.examples.Sequencer,
0980     spacePoints: str,
0981     seedingAlgorithmConfigArg: SeedingAlgorithmConfigArg,
0982     seedFinderConfigArg: SeedFinderConfigArg,
0983     seedFinderOptionsArg: SeedFinderOptionsArg,
0984     seedFilterConfigArg: SeedFilterConfigArg,
0985     spacePointGridConfigArg: SpacePointGridConfigArg,
0986     logLevel: acts.logging.Level = None,
0987     outputSeeds: str = "seeds",
0988 ):
0989     """adds orthogonal triplet seeding
0990     For parameters description see addSeeding
0991     """
0992     logLevel = acts.examples.defaultLogging(sequence, logLevel)()
0993 
0994     seedingAlg = acts.examples.OrthogonalTripletSeedingAlgorithm(
0995         level=logLevel,
0996         inputSpacePoints=spacePoints,
0997         outputSeeds=outputSeeds,
0998         **acts.examples.defaultKWArgs(
0999             bFieldInZ=seedFinderOptionsArg.bFieldInZ,
1000             minPt=seedFinderConfigArg.minPt,
1001             cotThetaMax=seedFinderConfigArg.cotThetaMax,
1002             impactMax=seedFinderConfigArg.impactMax,
1003             deltaRMin=seedFinderConfigArg.deltaR[0],
1004             deltaRMax=seedFinderConfigArg.deltaR[1],
1005             deltaRMinTop=(
1006                 seedFinderConfigArg.deltaR[0]
1007                 if seedFinderConfigArg.deltaRTopSP[0] is None
1008                 else seedFinderConfigArg.deltaRTopSP[0]
1009             ),
1010             deltaRMaxTop=(
1011                 seedFinderConfigArg.deltaR[1]
1012                 if seedFinderConfigArg.deltaRTopSP[1] is None
1013                 else seedFinderConfigArg.deltaRTopSP[1]
1014             ),
1015             deltaRMinBottom=(
1016                 seedFinderConfigArg.deltaR[0]
1017                 if seedFinderConfigArg.deltaRBottomSP[0] is None
1018                 else seedFinderConfigArg.deltaRBottomSP[0]
1019             ),
1020             deltaRMaxBottom=(
1021                 seedFinderConfigArg.deltaR[1]
1022                 if seedFinderConfigArg.deltaRBottomSP[1] is None
1023                 else seedFinderConfigArg.deltaRBottomSP[1]
1024             ),
1025             rMin=seedFinderConfigArg.r[0],
1026             rMax=seedFinderConfigArg.r[1],
1027             zMin=seedFinderConfigArg.z[0],
1028             zMax=seedFinderConfigArg.z[1],
1029             phiMin=spacePointGridConfigArg.phi[0],
1030             phiMax=spacePointGridConfigArg.phi[1],
1031             phiBinDeflectionCoverage=spacePointGridConfigArg.phiBinDeflectionCoverage,
1032             maxPhiBins=spacePointGridConfigArg.maxPhiBins,
1033             zBinEdges=spacePointGridConfigArg.zBinEdges,
1034             zBinsCustomLooping=seedFinderConfigArg.zBinsCustomLooping,
1035             rMinMiddle=None,
1036             rMaxMiddle=None,
1037             useVariableMiddleSPRange=seedFinderConfigArg.useVariableMiddleSPRange,
1038             rRangeMiddleSP=seedFinderConfigArg.rRangeMiddleSP,
1039             deltaRMiddleMinSPRange=seedFinderConfigArg.deltaRMiddleSPRange[0],
1040             deltaRMiddleMaxSPRange=seedFinderConfigArg.deltaRMiddleSPRange[1],
1041             deltaZMin=None,
1042             deltaZMax=None,
1043             interactionPointCut=seedFinderConfigArg.interactionPointCut,
1044             collisionRegionMin=seedFinderConfigArg.collisionRegion[0],
1045             collisionRegionMax=seedFinderConfigArg.collisionRegion[1],
1046             helixCutTolerance=None,
1047             sigmaScattering=seedFinderConfigArg.sigmaScattering,
1048             radLengthPerSeed=seedFinderConfigArg.radLengthPerSeed,
1049             toleranceParam=None,
1050             deltaInvHelixDiameter=None,
1051             compatSeedWeight=seedFilterConfigArg.compatSeedWeight,
1052             impactWeightFactor=seedFilterConfigArg.impactWeightFactor,
1053             zOriginWeightFactor=seedFilterConfigArg.zOriginWeightFactor,
1054             maxSeedsPerSpM=seedFinderConfigArg.maxSeedsPerSpM,
1055             compatSeedLimit=seedFilterConfigArg.compatSeedLimit,
1056             seedWeightIncrement=seedFilterConfigArg.seedWeightIncrement,
1057             numSeedIncrement=seedFilterConfigArg.numSeedIncrement,
1058             seedConfirmation=seedFinderConfigArg.seedConfirmation,
1059             centralSeedConfirmationRange=seedFinderConfigArg.centralSeedConfirmationRange,
1060             forwardSeedConfirmationRange=seedFinderConfigArg.forwardSeedConfirmationRange,
1061             maxSeedsPerSpMConf=seedFilterConfigArg.maxSeedsPerSpMConf,
1062             maxQualitySeedsPerSpMConf=seedFilterConfigArg.maxQualitySeedsPerSpMConf,
1063             useDeltaRinsteadOfTopRadius=seedFilterConfigArg.useDeltaRorTopRadius,
1064             useExtraCuts=seedingAlgorithmConfigArg.useExtraCuts,
1065         ),
1066     )
1067     sequence.addAlgorithm(seedingAlg)
1068 
1069     return seedingAlg.config.outputSeeds
1070 
1071 
1072 def addOrthogonalSeeding(
1073     sequence: acts.examples.Sequencer,
1074     spacePoints: str,
1075     seedFinderConfigArg: SeedFinderConfigArg,
1076     seedFinderOptionsArg: SeedFinderOptionsArg,
1077     seedFilterConfigArg: SeedFilterConfigArg,
1078     logLevel: acts.logging.Level = None,
1079 ):
1080     """adds orthogonal seeding algorithm
1081     For parameters description see addSeeding
1082     """
1083     logLevel = acts.examples.defaultLogging(sequence, logLevel)()
1084     seedFinderConfig = acts.examples.SeedFinderOrthogonalConfig(
1085         **acts.examples.defaultKWArgs(
1086             rMin=seedFinderConfigArg.r[0],
1087             rMax=seedFinderConfigArg.r[1],
1088             deltaRMinTopSP=(
1089                 seedFinderConfigArg.deltaR[0]
1090                 if seedFinderConfigArg.deltaRTopSP[0] is None
1091                 else seedFinderConfigArg.deltaRTopSP[0]
1092             ),
1093             deltaRMaxTopSP=(
1094                 seedFinderConfigArg.deltaR[1]
1095                 if seedFinderConfigArg.deltaRTopSP[1] is None
1096                 else seedFinderConfigArg.deltaRTopSP[1]
1097             ),
1098             deltaRMinBottomSP=(
1099                 seedFinderConfigArg.deltaR[0]
1100                 if seedFinderConfigArg.deltaRBottomSP[0] is None
1101                 else seedFinderConfigArg.deltaRBottomSP[0]
1102             ),
1103             deltaRMaxBottomSP=(
1104                 seedFinderConfigArg.deltaR[1]
1105                 if seedFinderConfigArg.deltaRBottomSP[1] is None
1106                 else seedFinderConfigArg.deltaRBottomSP[1]
1107             ),
1108             collisionRegionMin=seedFinderConfigArg.collisionRegion[0],
1109             collisionRegionMax=seedFinderConfigArg.collisionRegion[1],
1110             zMin=seedFinderConfigArg.z[0],
1111             zMax=seedFinderConfigArg.z[1],
1112             maxSeedsPerSpM=seedFinderConfigArg.maxSeedsPerSpM,
1113             cotThetaMax=seedFinderConfigArg.cotThetaMax,
1114             sigmaScattering=seedFinderConfigArg.sigmaScattering,
1115             radLengthPerSeed=seedFinderConfigArg.radLengthPerSeed,
1116             minPt=seedFinderConfigArg.minPt,
1117             impactMax=seedFinderConfigArg.impactMax,
1118             deltaPhiMax=seedFinderConfigArg.deltaPhiMax,
1119             interactionPointCut=seedFinderConfigArg.interactionPointCut,
1120             deltaZMax=seedFinderConfigArg.deltaZMax,
1121             maxPtScattering=seedFinderConfigArg.maxPtScattering,
1122             rRangeMiddleSP=seedFinderConfigArg.rRangeMiddleSP,
1123             useVariableMiddleSPRange=seedFinderConfigArg.useVariableMiddleSPRange,
1124             seedConfirmation=seedFinderConfigArg.seedConfirmation,
1125             centralSeedConfirmationRange=seedFinderConfigArg.centralSeedConfirmationRange,
1126             forwardSeedConfirmationRange=seedFinderConfigArg.forwardSeedConfirmationRange,
1127         ),
1128     )
1129     seedFinderOptions = acts.SeedFinderOptions(
1130         **acts.examples.defaultKWArgs(
1131             beamPos=(
1132                 acts.Vector2(0.0, 0.0)
1133                 if seedFinderOptionsArg.beamPos == (None, None)
1134                 else acts.Vector2(
1135                     seedFinderOptionsArg.beamPos[0], seedFinderOptionsArg.beamPos[1]
1136                 )
1137             ),
1138             bFieldInZ=seedFinderOptionsArg.bFieldInZ,
1139         )
1140     )
1141     seedFilterConfig = acts.SeedFilterConfig(
1142         **acts.examples.defaultKWArgs(
1143             maxSeedsPerSpM=seedFinderConfig.maxSeedsPerSpM,
1144             deltaRMin=(
1145                 seedFinderConfigArg.deltaR[0]
1146                 if seedFilterConfigArg.deltaRMin is None
1147                 else seedFilterConfigArg.deltaRMin
1148             ),
1149             impactWeightFactor=seedFilterConfigArg.impactWeightFactor,
1150             zOriginWeightFactor=seedFilterConfigArg.zOriginWeightFactor,
1151             compatSeedWeight=seedFilterConfigArg.compatSeedWeight,
1152             compatSeedLimit=seedFilterConfigArg.compatSeedLimit,
1153             numSeedIncrement=seedFilterConfigArg.numSeedIncrement,
1154             seedWeightIncrement=seedFilterConfigArg.seedWeightIncrement,
1155             seedConfirmation=seedFilterConfigArg.seedConfirmation,
1156             maxSeedsPerSpMConf=seedFilterConfigArg.maxSeedsPerSpMConf,
1157             maxQualitySeedsPerSpMConf=seedFilterConfigArg.maxQualitySeedsPerSpMConf,
1158             useDeltaRorTopRadius=seedFilterConfigArg.useDeltaRorTopRadius,
1159         )
1160     )
1161     seedingAlg = acts.examples.SeedingOrthogonalAlgorithm(
1162         level=logLevel,
1163         inputSpacePoints=[spacePoints],
1164         outputSeeds="seeds",
1165         seedFilterConfig=seedFilterConfig,
1166         seedFinderConfig=seedFinderConfig,
1167         seedFinderOptions=seedFinderOptions,
1168     )
1169     sequence.addAlgorithm(seedingAlg)
1170 
1171     return seedingAlg.config.outputSeeds
1172 
1173 
1174 def addHashingSeeding(
1175     sequence: acts.examples.Sequencer,
1176     spacePoints: str,
1177     seedingAlgorithmConfigArg: SeedingAlgorithmConfigArg,
1178     seedFinderConfigArg: SeedFinderConfigArg,
1179     seedFinderOptionsArg: SeedFinderOptionsArg,
1180     seedFilterConfigArg: SeedFilterConfigArg,
1181     spacePointGridConfigArg: SpacePointGridConfigArg,
1182     hashingTrainingConfigArg: HashingTrainingConfigArg,
1183     hashingAlgorithmConfigArg: HashingAlgorithmConfigArg,
1184     logLevel: acts.logging.Level = None,
1185 ):
1186     """adds Hashing seeding
1187     For parameters description see addSeeding docstring
1188     """
1189     logLevel = acts.examples.defaultLogging(sequence, logLevel)()
1190     from acts.examples.hashing import SeedingAlgorithmHashing
1191 
1192     # Same configuration than the standard seeding
1193     seedFinderConfig = acts.examples.SeedFinderConfig(
1194         **acts.examples.defaultKWArgs(
1195             rMin=seedFinderConfigArg.r[0],
1196             rMax=seedFinderConfigArg.r[1],
1197             deltaRMin=seedFinderConfigArg.deltaR[0],
1198             deltaRMax=seedFinderConfigArg.deltaR[1],
1199             deltaRMinTopSP=(
1200                 seedFinderConfigArg.deltaR[0]
1201                 if seedFinderConfigArg.deltaRTopSP[0] is None
1202                 else seedFinderConfigArg.deltaRTopSP[0]
1203             ),
1204             deltaRMaxTopSP=(
1205                 seedFinderConfigArg.deltaR[1]
1206                 if seedFinderConfigArg.deltaRTopSP[1] is None
1207                 else seedFinderConfigArg.deltaRTopSP[1]
1208             ),
1209             deltaRMinBottomSP=(
1210                 seedFinderConfigArg.deltaR[0]
1211                 if seedFinderConfigArg.deltaRBottomSP[0] is None
1212                 else seedFinderConfigArg.deltaRBottomSP[0]
1213             ),
1214             deltaRMaxBottomSP=(
1215                 seedFinderConfigArg.deltaR[1]
1216                 if seedFinderConfigArg.deltaRBottomSP[1] is None
1217                 else seedFinderConfigArg.deltaRBottomSP[1]
1218             ),
1219             deltaRMiddleMinSPRange=seedFinderConfigArg.deltaRMiddleSPRange[0],
1220             deltaRMiddleMaxSPRange=seedFinderConfigArg.deltaRMiddleSPRange[1],
1221             collisionRegionMin=seedFinderConfigArg.collisionRegion[0],
1222             collisionRegionMax=seedFinderConfigArg.collisionRegion[1],
1223             zMin=seedFinderConfigArg.z[0],
1224             zMax=seedFinderConfigArg.z[1],
1225             maxSeedsPerSpM=seedFinderConfigArg.maxSeedsPerSpM,
1226             cotThetaMax=seedFinderConfigArg.cotThetaMax,
1227             sigmaScattering=seedFinderConfigArg.sigmaScattering,
1228             radLengthPerSeed=seedFinderConfigArg.radLengthPerSeed,
1229             minPt=seedFinderConfigArg.minPt,
1230             impactMax=seedFinderConfigArg.impactMax,
1231             interactionPointCut=seedFinderConfigArg.interactionPointCut,
1232             deltaZMax=seedFinderConfigArg.deltaZMax,
1233             maxPtScattering=seedFinderConfigArg.maxPtScattering,
1234             zBinEdges=seedFinderConfigArg.zBinEdges,
1235             zBinsCustomLooping=seedFinderConfigArg.zBinsCustomLooping,
1236             rRangeMiddleSP=seedFinderConfigArg.rRangeMiddleSP,
1237             useVariableMiddleSPRange=seedFinderConfigArg.useVariableMiddleSPRange,
1238             binSizeR=seedFinderConfigArg.binSizeR,
1239             seedConfirmation=seedFinderConfigArg.seedConfirmation,
1240             centralSeedConfirmationRange=seedFinderConfigArg.centralSeedConfirmationRange,
1241             forwardSeedConfirmationRange=seedFinderConfigArg.forwardSeedConfirmationRange,
1242         ),
1243     )
1244     seedFinderOptions = acts.SeedFinderOptions(
1245         **acts.examples.defaultKWArgs(
1246             beamPos=(
1247                 acts.Vector2(0.0, 0.0)
1248                 if seedFinderOptionsArg.beamPos == (None, None)
1249                 else acts.Vector2(
1250                     seedFinderOptionsArg.beamPos[0], seedFinderOptionsArg.beamPos[1]
1251                 )
1252             ),
1253             bFieldInZ=seedFinderOptionsArg.bFieldInZ,
1254         )
1255     )
1256     seedFilterConfig = acts.SeedFilterConfig(
1257         **acts.examples.defaultKWArgs(
1258             maxSeedsPerSpM=seedFinderConfig.maxSeedsPerSpM,
1259             deltaRMin=(
1260                 seedFinderConfig.deltaRMin
1261                 if seedFilterConfigArg.deltaRMin is None
1262                 else seedFilterConfigArg.deltaRMin
1263             ),
1264             impactWeightFactor=seedFilterConfigArg.impactWeightFactor,
1265             zOriginWeightFactor=seedFilterConfigArg.zOriginWeightFactor,
1266             compatSeedWeight=seedFilterConfigArg.compatSeedWeight,
1267             compatSeedLimit=seedFilterConfigArg.compatSeedLimit,
1268             numSeedIncrement=seedFilterConfigArg.numSeedIncrement,
1269             seedWeightIncrement=seedFilterConfigArg.seedWeightIncrement,
1270             seedConfirmation=seedFilterConfigArg.seedConfirmation,
1271             centralSeedConfirmationRange=seedFinderConfig.centralSeedConfirmationRange,
1272             forwardSeedConfirmationRange=seedFinderConfig.forwardSeedConfirmationRange,
1273             maxSeedsPerSpMConf=seedFilterConfigArg.maxSeedsPerSpMConf,
1274             maxQualitySeedsPerSpMConf=seedFilterConfigArg.maxQualitySeedsPerSpMConf,
1275             useDeltaRorTopRadius=seedFilterConfigArg.useDeltaRorTopRadius,
1276         )
1277     )
1278 
1279     gridConfig = acts.SpacePointGridConfig(
1280         **acts.examples.defaultKWArgs(
1281             minPt=seedFinderConfig.minPt,
1282             rMax=(
1283                 seedFinderConfig.rMax
1284                 if spacePointGridConfigArg.rMax is None
1285                 else spacePointGridConfigArg.rMax
1286             ),
1287             zMax=seedFinderConfig.zMax,
1288             zMin=seedFinderConfig.zMin,
1289             deltaRMax=(
1290                 seedFinderConfig.deltaRMax
1291                 if spacePointGridConfigArg.deltaRMax is None
1292                 else spacePointGridConfigArg.deltaRMax
1293             ),
1294             cotThetaMax=seedFinderConfig.cotThetaMax,
1295             phiMin=spacePointGridConfigArg.phi[0],
1296             phiMax=spacePointGridConfigArg.phi[1],
1297             maxPhiBins=spacePointGridConfigArg.maxPhiBins,
1298             impactMax=spacePointGridConfigArg.impactMax,
1299             zBinEdges=spacePointGridConfigArg.zBinEdges,
1300             phiBinDeflectionCoverage=spacePointGridConfigArg.phiBinDeflectionCoverage,
1301         )
1302     )
1303 
1304     gridOptions = acts.SpacePointGridOptions(
1305         **acts.examples.defaultKWArgs(
1306             bFieldInZ=seedFinderOptions.bFieldInZ,
1307         )
1308     )
1309 
1310     # Hashing configuration
1311     hashingTrainingConfig = acts.hashing.HashingTrainingConfig(
1312         **acts.examples.defaultKWArgs(
1313             annoySeed=hashingTrainingConfigArg.annoySeed,
1314             f=hashingTrainingConfigArg.f,
1315         ),
1316     )
1317 
1318     hashingConfig = acts.hashing.HashingAlgorithmConfig(
1319         **acts.examples.defaultKWArgs(
1320             bucketSize=hashingAlgorithmConfigArg.bucketSize,
1321             zBins=hashingAlgorithmConfigArg.zBins,
1322             phiBins=hashingAlgorithmConfigArg.phiBins,
1323         ),
1324     )
1325 
1326     # Seeding algorithm
1327     seedingAlg = SeedingAlgorithmHashing(
1328         level=logLevel,
1329         inputSpacePoints=[spacePoints],
1330         outputSeeds="seeds",
1331         outputBuckets="buckets",
1332         **acts.examples.defaultKWArgs(
1333             allowSeparateRMax=seedingAlgorithmConfigArg.allowSeparateRMax,
1334             zBinNeighborsTop=seedingAlgorithmConfigArg.zBinNeighborsTop,
1335             zBinNeighborsBottom=seedingAlgorithmConfigArg.zBinNeighborsBottom,
1336             numPhiNeighbors=seedingAlgorithmConfigArg.numPhiNeighbors,
1337         ),
1338         gridConfig=gridConfig,
1339         gridOptions=gridOptions,
1340         seedFilterConfig=seedFilterConfig,
1341         seedFinderConfig=seedFinderConfig,
1342         seedFinderOptions=seedFinderOptions,
1343         hashingConfig=hashingConfig,
1344         hashingTrainingConfig=hashingTrainingConfig,
1345     )
1346     sequence.addAlgorithm(seedingAlg)
1347 
1348     return seedingAlg.config.outputSeeds, seedingAlg.config.outputBuckets
1349 
1350 
1351 def addHoughTransformSeeding(
1352     sequence: acts.examples.Sequencer,
1353     config: acts.examples.HoughTransformSeeder.Config,
1354     logLevel: acts.logging.Level = None,
1355 ):
1356     """
1357     Configures HoughTransform (HT) for seeding, instead of extra proxy config objects it takes
1358     directly the HT example algorithm config.
1359     """
1360     logLevel = acts.examples.defaultLogging(sequence, logLevel)()
1361     ht = acts.examples.HoughTransformSeeder(config=config, level=logLevel)
1362     sequence.addAlgorithm(ht)
1363     # potentially HT can be extended to also produce seeds, but it is not implemented yet
1364     # configuration option (outputSeeds) exists
1365     return ht.config.outputSeeds
1366 
1367 
1368 def addAdaptiveHoughTransformSeeding(
1369     sequence: acts.examples.Sequencer,
1370     config: acts.examples.AdaptiveHoughTransformSeeder.Config,
1371     logLevel: acts.logging.Level = None,
1372 ):
1373     """
1374     Configures AdaptiveHoughTransform (AHT) for seeding, instead of extra proxy config objects it takes
1375     directly the AHT example algorithm config.
1376     """
1377     logLevel = acts.examples.defaultLogging(sequence, logLevel)()
1378     ht = acts.examples.AdaptiveHoughTransformSeeder(config=config, level=logLevel)
1379     sequence.addAlgorithm(ht)
1380     # potentially HT can be extended to also produce proto-tracks, but it is not implemented yet
1381     # configuration option (outputSeeds) exists and is used
1382     return ht.config.outputSeeds
1383 
1384 
1385 def addGbtsSeeding(
1386     sequence: acts.examples.Sequencer,
1387     spacePoints: str,
1388     seedFinderConfigArg: SeedFinderConfigArg,
1389     seedFinderOptionsArg: SeedFinderOptionsArg,
1390     trackingGeometry: acts.TrackingGeometry,
1391     logLevel: acts.logging.Level = None,
1392     layerMappingConfigFile: Union[Path, str] = None,
1393     geoSelectionConfigFile: Union[Path, str] = None,
1394     ConnectorInputConfigFile: Union[Path, str] = None,
1395 ):
1396     """Gbts seeding"""
1397 
1398     logLevel = acts.examples.defaultLogging(sequence, logLevel)()
1399     layerMappingFile = str(layerMappingConfigFile)  # turn path into string
1400     ConnectorInputFileStr = str(ConnectorInputConfigFile)
1401     seedFinderConfig = acts.examples.SeedFinderGbtsConfig(
1402         **acts.examples.defaultKWArgs(
1403             minPt=seedFinderConfigArg.minPt, ConnectorInputFile=ConnectorInputFileStr
1404         ),
1405     )
1406     seedFinderOptions = acts.SeedFinderOptions(
1407         **acts.examples.defaultKWArgs(
1408             beamPos=(
1409                 acts.Vector2(0.0, 0.0)
1410                 if seedFinderOptionsArg.beamPos == (None, None)
1411                 else acts.Vector2(
1412                     seedFinderOptionsArg.beamPos[0], seedFinderOptionsArg.beamPos[1]
1413                 )
1414             ),
1415             bFieldInZ=seedFinderOptionsArg.bFieldInZ,
1416         )
1417     )
1418 
1419     seedingAlg = acts.examples.GbtsSeedingAlgorithm(
1420         level=logLevel,
1421         inputSpacePoints=spacePoints,
1422         outputSeeds="seeds",
1423         seedFinderConfig=seedFinderConfig,
1424         seedFinderOptions=seedFinderOptions,
1425         layerMappingFile=layerMappingFile,
1426         trackingGeometry=trackingGeometry,
1427         fill_module_csv=False,
1428         inputClusters="clusters",
1429     )
1430 
1431     sequence.addAlgorithm(seedingAlg)
1432     return seedingAlg.config.outputSeeds
1433 
1434 
1435 def addSeedPerformanceWriters(
1436     sequence: acts.examples.Sequencer,
1437     outputDirRoot: Union[Path, str],
1438     tracks: str,
1439     prototracks: str,
1440     selectedParticles: str,
1441     inputParticles: str,
1442     outputTrackParameters: str,
1443     logLevel: acts.logging.Level = None,
1444 ):
1445     """Writes seeding related performance output"""
1446     customLogLevel = acts.examples.defaultLogging(sequence, logLevel)
1447     outputDirRoot = Path(outputDirRoot)
1448     if not outputDirRoot.exists():
1449         outputDirRoot.mkdir()
1450 
1451     sequence.addWriter(
1452         acts.examples.root.RootTrackFinderPerformanceWriter(
1453             level=customLogLevel(),
1454             inputTracks=tracks,
1455             inputParticles=selectedParticles,
1456             inputTrackParticleMatching="seed_particle_matching",
1457             inputParticleTrackMatching="particle_seed_matching",
1458             inputParticleMeasurementsMap="particle_measurements_map",
1459             filePath=str(outputDirRoot / f"performance_seeding.root"),
1460         )
1461     )
1462 
1463     sequence.addWriter(
1464         acts.examples.root.RootTrackParameterWriter(
1465             level=customLogLevel(),
1466             inputTrackParameters=outputTrackParameters,
1467             inputProtoTracks=prototracks,
1468             inputParticles=inputParticles,
1469             inputSimHits="simhits",
1470             inputMeasurementParticlesMap="measurement_particles_map",
1471             inputMeasurementSimHitsMap="measurement_simhits_map",
1472             filePath=str(outputDirRoot / "estimatedparams.root"),
1473             treeName="estimatedparams",
1474         )
1475     )
1476 
1477 
1478 acts.examples.NamedTypeArgs(
1479     config=SeedFilterMLDBScanConfig,
1480 )
1481 
1482 
1483 def addSeedFilterML(
1484     s,
1485     config: SeedFilterMLDBScanConfig = SeedFilterMLDBScanConfig(),
1486     onnxModelFile: Optional[Union[Path, str]] = None,
1487     logLevel: Optional[acts.logging.Level] = None,
1488     outputDirRoot: Optional[Union[Path, str]] = None,
1489     outputDirCsv: Optional[Union[Path, str]] = None,
1490 ) -> None:
1491     customLogLevel = acts.examples.defaultLogging(s, logLevel)()
1492     from acts.examples.onnx import SeedFilterMLAlgorithm
1493 
1494     inputParticles = "particles"
1495     selectedParticles = "particles_selected"
1496     seeds = "seeds"
1497     estParams = "estimatedparameters"
1498     prototracks = "seed-prototracks-ML"
1499     tracks = "seed-tracks-ML"
1500 
1501     filterML = SeedFilterMLAlgorithm(
1502         level=customLogLevel,
1503         inputTrackParameters="estimatedparameters",
1504         inputSimSeeds="seeds",
1505         inputSeedFilterNN=onnxModelFile,
1506         outputTrackParameters="filtered-parameters",
1507         outputSimSeeds="filtered-seeds",
1508         **acts.examples.defaultKWArgs(
1509             epsilonDBScan=config.epsilonDBScan,
1510             minPointsDBScan=config.minPointsDBScan,
1511             minSeedScore=config.minSeedScore,
1512         ),
1513     )
1514     s.addAlgorithm(filterML)
1515     s.addWhiteboardAlias(seeds, "filtered-seeds")
1516     s.addWhiteboardAlias("estimatedparameters", "filtered-parameters")
1517 
1518     s.addAlgorithm(
1519         acts.examples.SeedsToPrototracks(
1520             level=customLogLevel,
1521             inputSeeds=seeds,
1522             outputProtoTracks=prototracks,
1523         )
1524     )
1525 
1526     s.addAlgorithm(
1527         acts.examples.PrototracksToTracks(
1528             level=customLogLevel,
1529             inputProtoTracks=prototracks,
1530             inputTrackParameters="estimatedparameters",
1531             outputTracks=tracks,
1532         )
1533     )
1534 
1535     s.addAlgorithm(
1536         acts.examples.TrackTruthMatcher(
1537             level=customLogLevel,
1538             inputTracks=tracks,
1539             inputParticles=selectedParticles,
1540             inputMeasurementParticlesMap="measurement_particles_map",
1541             outputTrackParticleMatching="seed_particle_matching",
1542             outputParticleTrackMatching="particle_seed_matching",
1543             matchingRatio=1.0,
1544             doubleMatching=False,
1545         )
1546     )
1547 
1548     if outputDirRoot is not None:
1549         addSeedPerformanceWriters(
1550             s,
1551             outputDirRoot,
1552             tracks,
1553             selectedParticles,
1554             inputParticles,
1555             estParams,
1556             customLogLevel,
1557         )
1558 
1559     if outputDirCsv is not None:
1560         outputDirCsv = Path(outputDirCsv)
1561 
1562         if not outputDirCsv.exists():
1563             outputDirCsv.mkdir()
1564 
1565         csvSeedWriter = acts.examples.CsvSeedWriter(
1566             level=customLogLevel,
1567             inputTrackParameters=estParams,
1568             inputSimSeeds=seeds,
1569             inputSimHits="simhits",
1570             inputMeasurementParticlesMap="measurement_particles_map",
1571             inputMeasurementSimHitsMap="measurement_simhits_map",
1572             outputDir=str(outputDirCsv),
1573             fileName=str(f"seed.csv"),
1574         )
1575         s.addWriter(csvSeedWriter)
1576 
1577     return s
1578 
1579 
1580 def addKalmanTracks(
1581     s: acts.examples.Sequencer,
1582     trackingGeometry: acts.TrackingGeometry,
1583     field: acts.MagneticFieldProvider,
1584     reverseFilteringMomThreshold: float = 0 * u.GeV,
1585     reverseFilteringCovarianceScaling: float = 1.0,
1586     inputProtoTracks: str = "truth_particle_tracks",
1587     multipleScattering: bool = True,
1588     energyLoss: bool = True,
1589     clusters: str = None,
1590     calibrator: acts.examples.MeasurementCalibrator = acts.examples.makePassThroughCalibrator(),
1591     logLevel: Optional[acts.logging.Level] = None,
1592 ) -> None:
1593     customLogLevel = acts.examples.defaultLogging(s, logLevel)
1594 
1595     kalmanOptions = {
1596         "multipleScattering": multipleScattering,
1597         "energyLoss": energyLoss,
1598         "reverseFilteringMomThreshold": reverseFilteringMomThreshold,
1599         "reverseFilteringCovarianceScaling": reverseFilteringCovarianceScaling,
1600         "freeToBoundCorrection": acts.examples.FreeToBoundCorrection(False),
1601         "level": customLogLevel(),
1602         "chi2Cut": float("inf"),
1603     }
1604 
1605     fitAlg = acts.examples.TrackFittingAlgorithm(
1606         level=customLogLevel(),
1607         inputMeasurements="measurements",
1608         inputProtoTracks=inputProtoTracks,
1609         inputInitialTrackParameters="estimatedparameters",
1610         inputClusters=clusters if clusters is not None else "",
1611         outputTracks="kf_tracks",
1612         pickTrack=-1,
1613         fit=acts.examples.makeKalmanFitterFunction(
1614             trackingGeometry, field, **kalmanOptions
1615         ),
1616         calibrator=calibrator,
1617     )
1618     s.addAlgorithm(fitAlg)
1619     s.addWhiteboardAlias("tracks", fitAlg.config.outputTracks)
1620 
1621     matchAlg = acts.examples.TrackTruthMatcher(
1622         level=customLogLevel(),
1623         inputTracks=fitAlg.config.outputTracks,
1624         inputParticles="particles",
1625         inputMeasurementParticlesMap="measurement_particles_map",
1626         outputTrackParticleMatching="kf_track_particle_matching",
1627         outputParticleTrackMatching="kf_particle_track_matching",
1628         doubleMatching=True,
1629     )
1630     s.addAlgorithm(matchAlg)
1631     s.addWhiteboardAlias(
1632         "track_particle_matching", matchAlg.config.outputTrackParticleMatching
1633     )
1634     s.addWhiteboardAlias(
1635         "particle_track_matching", matchAlg.config.outputParticleTrackMatching
1636     )
1637 
1638     return s
1639 
1640 
1641 def addTruthTrackingGsf(
1642     s: acts.examples.Sequencer,
1643     trackingGeometry: acts.TrackingGeometry,
1644     field: acts.MagneticFieldProvider,
1645     inputProtoTracks: str = "truth_particle_tracks",
1646     logLevel: Optional[acts.logging.Level] = None,
1647 ) -> None:
1648     customLogLevel = acts.examples.defaultLogging(s, logLevel)
1649 
1650     # NOTE we specify clampToRange as True to silence warnings in the test about
1651     # queries to the loss distribution outside the specified range, since no dedicated
1652     # approximation for the ODD is done yet.
1653     bha = acts.examples.AtlasBetheHeitlerApprox.makeDefault(clampToRange=True)
1654 
1655     gsfOptions = {
1656         "betheHeitlerApprox": bha,
1657         "maxComponents": 12,
1658         "componentMergeMethod": acts.examples.ComponentMergeMethod.maxWeight,
1659         "mixtureReductionAlgorithm": acts.examples.MixtureReductionAlgorithm.KLDistance,
1660         "weightCutoff": 1.0e-4,
1661         "reverseFilteringCovarianceScaling": 100.0,
1662         "level": customLogLevel(),
1663     }
1664 
1665     gsfAlg = acts.examples.TrackFittingAlgorithm(
1666         level=customLogLevel(),
1667         inputMeasurements="measurements",
1668         inputProtoTracks=inputProtoTracks,
1669         inputInitialTrackParameters="estimatedparameters",
1670         outputTracks="gsf_tracks",
1671         pickTrack=-1,
1672         fit=acts.examples.makeGsfFitterFunction(trackingGeometry, field, **gsfOptions),
1673         calibrator=acts.examples.makePassThroughCalibrator(),
1674     )
1675     s.addAlgorithm(gsfAlg)
1676     s.addWhiteboardAlias("tracks", gsfAlg.config.outputTracks)
1677 
1678     matchAlg = acts.examples.TrackTruthMatcher(
1679         level=customLogLevel(),
1680         inputTracks=gsfAlg.config.outputTracks,
1681         inputParticles="particles",
1682         inputMeasurementParticlesMap="measurement_particles_map",
1683         outputTrackParticleMatching="gsf_track_particle_matching",
1684         outputParticleTrackMatching="gsf_particle_track_matching",
1685         doubleMatching=True,
1686     )
1687     s.addAlgorithm(matchAlg)
1688     s.addWhiteboardAlias(
1689         "track_particle_matching", matchAlg.config.outputTrackParticleMatching
1690     )
1691     s.addWhiteboardAlias(
1692         "particle_track_matching", matchAlg.config.outputParticleTrackMatching
1693     )
1694 
1695     return s
1696 
1697 
1698 @acts.examples.NamedTypeArgs(
1699     trackSelectorConfig=TrackSelectorConfig,
1700     ckfConfig=CkfConfig,
1701 )
1702 def addCKFTracks(
1703     s: acts.examples.Sequencer,
1704     trackingGeometry: acts.TrackingGeometry,
1705     field: acts.MagneticFieldProvider,
1706     trackSelectorConfig: Optional[
1707         Union[TrackSelectorConfig, List[TrackSelectorConfig]]
1708     ] = None,
1709     ckfConfig: CkfConfig = CkfConfig(),
1710     twoWay: bool = True,
1711     reverseSearch: bool = False,
1712     outputDirCsv: Optional[Union[Path, str]] = None,
1713     outputDirRoot: Optional[Union[Path, str]] = None,
1714     writeTrackSummary: bool = True,
1715     writeTrackStates: bool = False,
1716     writePerformance: bool = True,
1717     writeCovMat=False,
1718     logLevel: Optional[acts.logging.Level] = None,
1719 ) -> None:
1720     """This function steers the seeding
1721 
1722     Parameters
1723     ----------
1724     s: Sequencer
1725         the sequencer module to which we add the Seeding steps (returned from addSeeding)
1726     trackingGeometry : tracking geometry
1727     field : magnetic field
1728     outputDirCsv : Path|str, path, None
1729         the output folder for the Csv output, None triggers no output
1730     outputDirRoot : Path|str, path, None
1731         the output folder for ROOT output, None triggers no output
1732     trackSelectorConfig : TrackSelectorConfig(loc0, loc1, time, eta, absEta, pt, phi, minMeasurements)
1733         TrackSelector configuration. Each range is specified as a tuple of (min,max).
1734         Specify as a list(TrackSelectorConfig) for eta-dependent cuts, with binning specified by absEta[1].
1735         Defaults of no cuts specified in Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/TrackSelector.hpp
1736     writeTrackSummary : bool, True
1737         write tracksummary_ckf.root ntuple?
1738     writeTrackStates : bool, False
1739         write trackstates_ckf.root ntuple? This can be quite large.
1740     writePerformance : bool, True
1741         write performance_fitting_ckf.root and performance_finding_ckf.root ntuples?
1742     writeCovMat : bool, False
1743         write covaraiance matrices to tracksummary_ckf.root ntuple?
1744     """
1745 
1746     customLogLevel = acts.examples.defaultLogging(s, logLevel)
1747 
1748     tslist = (
1749         []
1750         if trackSelectorConfig is None
1751         else (
1752             [trackSelectorConfig]
1753             if type(trackSelectorConfig) is TrackSelectorConfig
1754             else trackSelectorConfig
1755         )
1756     )
1757 
1758     if len(tslist) > 1:
1759         cutSets = []
1760         for c in tslist:
1761             defKW = trackSelectorDefaultKWArgs(c)
1762             defKW.pop("absEtaMax", None)
1763             cutSets += [acts.TrackSelector.Config(**(defKW))]
1764     else:
1765         cutSets = [
1766             acts.TrackSelector.Config(**(trackSelectorDefaultKWArgs(c))) for c in tslist
1767         ]
1768 
1769     if len(tslist) == 0:
1770         trkSelCfg = None
1771     elif len(tslist) == 1:
1772         trkSelCfg = cutSets[0]
1773     else:
1774         trkSelCfg = acts.TrackSelector.EtaBinnedConfig(
1775             cutSets=cutSets,
1776             absEtaEdges=[cutSets[0].absEtaMin] + [c.absEta[1] for c in tslist],
1777         )
1778 
1779     # Setup the track finding algorithm with CKF
1780     # It takes all the source links created from truth hit smearing, seeds from
1781     # truth particle smearing and source link selection config
1782     trackFinder = acts.examples.TrackFindingAlgorithm(
1783         level=customLogLevel(),
1784         measurementSelectorCfg=acts.MeasurementSelector.Config(
1785             [
1786                 (
1787                     acts.GeometryIdentifier(),
1788                     (
1789                         [],
1790                         [ckfConfig.chi2CutOffMeasurement],
1791                         [ckfConfig.chi2CutOffOutlier],
1792                         [ckfConfig.numMeasurementsCutOff],
1793                     ),
1794                 )
1795             ]
1796         ),
1797         inputMeasurements="measurements",
1798         inputInitialTrackParameters="estimatedparameters",
1799         inputSeeds=(
1800             "estimatedseeds"
1801             if ckfConfig.seedDeduplication or ckfConfig.stayOnSeed
1802             else ""
1803         ),
1804         outputTracks="ckf_tracks",
1805         findTracks=acts.examples.TrackFindingAlgorithm.makeTrackFinderFunction(
1806             trackingGeometry, field, customLogLevel()
1807         ),
1808         **acts.examples.defaultKWArgs(
1809             trackingGeometry=trackingGeometry,
1810             magneticField=field,
1811             trackSelectorCfg=trkSelCfg,
1812             maxSteps=ckfConfig.maxSteps,
1813             twoWay=twoWay,
1814             reverseSearch=reverseSearch,
1815             seedDeduplication=ckfConfig.seedDeduplication,
1816             stayOnSeed=ckfConfig.stayOnSeed,
1817             pixelVolumeIds=ckfConfig.pixelVolumes,
1818             stripVolumeIds=ckfConfig.stripVolumes,
1819             maxPixelHoles=ckfConfig.maxPixelHoles,
1820             maxStripHoles=ckfConfig.maxStripHoles,
1821             trimTracks=ckfConfig.trimTracks,
1822             constrainToVolumeIds=ckfConfig.constrainToVolumes,
1823             endOfWorldVolumeIds=ckfConfig.endOfWorldVolumes,
1824         ),
1825     )
1826     s.addAlgorithm(trackFinder)
1827     s.addWhiteboardAlias("tracks", trackFinder.config.outputTracks)
1828 
1829     matchAlg = acts.examples.TrackTruthMatcher(
1830         level=customLogLevel(),
1831         inputTracks=trackFinder.config.outputTracks,
1832         inputParticles="particles_selected",
1833         inputMeasurementParticlesMap="measurement_particles_map",
1834         outputTrackParticleMatching="ckf_track_particle_matching",
1835         outputParticleTrackMatching="ckf_particle_track_matching",
1836         doubleMatching=True,
1837     )
1838     s.addAlgorithm(matchAlg)
1839     s.addWhiteboardAlias(
1840         "track_particle_matching", matchAlg.config.outputTrackParticleMatching
1841     )
1842     s.addWhiteboardAlias(
1843         "particle_track_matching", matchAlg.config.outputParticleTrackMatching
1844     )
1845 
1846     addTrackWriters(
1847         s,
1848         name="ckf",
1849         tracks=trackFinder.config.outputTracks,
1850         outputDirCsv=outputDirCsv,
1851         outputDirRoot=outputDirRoot,
1852         writeSummary=writeTrackSummary,
1853         writeStates=writeTrackStates,
1854         writeFitterPerformance=writePerformance,
1855         writeFinderPerformance=writePerformance,
1856         writeCovMat=writeCovMat,
1857         logLevel=logLevel,
1858     )
1859 
1860     return s
1861 
1862 
1863 def addGx2fTracks(
1864     s: acts.examples.Sequencer,
1865     trackingGeometry: acts.TrackingGeometry,
1866     field: acts.MagneticFieldProvider,
1867     inputProtoTracks: str = "truth_particle_tracks",
1868     multipleScattering: bool = False,
1869     energyLoss: bool = False,
1870     nUpdateMax: int = 5,
1871     relChi2changeCutOff: float = 1e-7,
1872     clusters: str = None,
1873     calibrator: acts.examples.MeasurementCalibrator = acts.examples.makePassThroughCalibrator(),
1874     logLevel: Optional[acts.logging.Level] = None,
1875 ) -> None:
1876     customLogLevel = acts.examples.defaultLogging(s, logLevel)
1877 
1878     gx2fOptions = {
1879         "multipleScattering": multipleScattering,
1880         "energyLoss": energyLoss,
1881         "freeToBoundCorrection": acts.examples.FreeToBoundCorrection(False),
1882         "nUpdateMax": nUpdateMax,
1883         "relChi2changeCutOff": relChi2changeCutOff,
1884         "level": customLogLevel(),
1885     }
1886 
1887     fitAlg = acts.examples.TrackFittingAlgorithm(
1888         level=customLogLevel(),
1889         inputMeasurements="measurements",
1890         inputProtoTracks=inputProtoTracks,
1891         inputInitialTrackParameters="estimatedparameters",
1892         inputClusters=clusters if clusters is not None else "",
1893         outputTracks="gx2f_tracks",
1894         pickTrack=-1,
1895         fit=acts.examples.makeGlobalChiSquareFitterFunction(
1896             trackingGeometry, field, **gx2fOptions
1897         ),
1898         calibrator=calibrator,
1899     )
1900     s.addAlgorithm(fitAlg)
1901     s.addWhiteboardAlias("tracks", fitAlg.config.outputTracks)
1902 
1903     matchAlg = acts.examples.TrackTruthMatcher(
1904         level=customLogLevel(),
1905         inputTracks=fitAlg.config.outputTracks,
1906         inputParticles="particles",
1907         inputMeasurementParticlesMap="measurement_particles_map",
1908         outputTrackParticleMatching="gx2f_track_particle_matching",
1909         outputParticleTrackMatching="gx2f_particle_track_matching",
1910         doubleMatching=True,
1911     )
1912     s.addAlgorithm(matchAlg)
1913     s.addWhiteboardAlias(
1914         "track_particle_matching", matchAlg.config.outputTrackParticleMatching
1915     )
1916     s.addWhiteboardAlias(
1917         "particle_track_matching", matchAlg.config.outputParticleTrackMatching
1918     )
1919 
1920     return s
1921 
1922 
1923 def addTrackWriters(
1924     s: acts.examples.Sequencer,
1925     name: str,
1926     tracks: str = "tracks",
1927     outputDirCsv: Optional[Union[Path, str]] = None,
1928     outputDirRoot: Optional[Union[Path, str]] = None,
1929     writeSummary: bool = True,
1930     writeStates: bool = False,
1931     writeFitterPerformance: bool = False,
1932     writeFinderPerformance: bool = False,
1933     logLevel: Optional[acts.logging.Level] = None,
1934     writeCovMat=False,
1935 ):
1936     customLogLevel = acts.examples.defaultLogging(s, logLevel)
1937 
1938     if outputDirRoot is not None:
1939         outputDirRoot = Path(outputDirRoot)
1940         if not outputDirRoot.exists():
1941             outputDirRoot.mkdir()
1942 
1943         if writeSummary:
1944             trackSummaryWriter = acts.examples.root.RootTrackSummaryWriter(
1945                 level=customLogLevel(),
1946                 inputTracks=tracks,
1947                 inputParticles="particles_selected",
1948                 inputTrackParticleMatching="track_particle_matching",
1949                 filePath=str(outputDirRoot / f"tracksummary_{name}.root"),
1950                 treeName="tracksummary",
1951                 writeCovMat=writeCovMat,
1952             )
1953             s.addWriter(trackSummaryWriter)
1954 
1955         if writeStates:
1956             trackStatesWriter = acts.examples.root.RootTrackStatesWriter(
1957                 level=customLogLevel(),
1958                 inputTracks=tracks,
1959                 inputParticles="particles_selected",
1960                 inputTrackParticleMatching="track_particle_matching",
1961                 inputSimHits="simhits",
1962                 inputMeasurementSimHitsMap="measurement_simhits_map",
1963                 filePath=str(outputDirRoot / f"trackstates_{name}.root"),
1964                 treeName="trackstates",
1965             )
1966             s.addWriter(trackStatesWriter)
1967 
1968         if writeFitterPerformance:
1969             trackFitterPerformanceWriter = (
1970                 acts.examples.root.RootTrackFitterPerformanceWriter(
1971                     level=customLogLevel(),
1972                     inputTracks=tracks,
1973                     inputParticles="particles_selected",
1974                     inputTrackParticleMatching="track_particle_matching",
1975                     filePath=str(outputDirRoot / f"performance_fitting_{name}.root"),
1976                 )
1977             )
1978             s.addWriter(trackFitterPerformanceWriter)
1979 
1980         if writeFinderPerformance:
1981             trackFinderPerfWriter = acts.examples.root.RootTrackFinderPerformanceWriter(
1982                 level=customLogLevel(),
1983                 inputTracks=tracks,
1984                 inputParticles="particles_selected",
1985                 inputTrackParticleMatching="track_particle_matching",
1986                 inputParticleTrackMatching="particle_track_matching",
1987                 inputParticleMeasurementsMap="particle_measurements_map",
1988                 filePath=str(outputDirRoot / f"performance_finding_{name}.root"),
1989             )
1990             s.addWriter(trackFinderPerfWriter)
1991 
1992     if outputDirCsv is not None:
1993         outputDirCsv = Path(outputDirCsv)
1994         if not outputDirCsv.exists():
1995             outputDirCsv.mkdir()
1996 
1997         if writeSummary:
1998             csvWriter = acts.examples.CsvTrackWriter(
1999                 level=customLogLevel(),
2000                 inputTracks=tracks,
2001                 inputMeasurementParticlesMap="measurement_particles_map",
2002                 outputDir=str(outputDirCsv),
2003                 fileName=str(f"tracks_{name}.csv"),
2004             )
2005             s.addWriter(csvWriter)
2006 
2007             trackParameterWriter = acts.examples.CsvTrackParameterWriter(
2008                 level=customLogLevel(),
2009                 inputTracks=tracks,
2010                 outputDir=str(outputDirCsv),
2011                 outputStem=str(f"track_parameters_{name}"),
2012             )
2013             s.addWriter(trackParameterWriter)
2014 
2015 
2016 @acts.examples.NamedTypeArgs(
2017     trackSelectorConfig=TrackSelectorConfig,
2018 )
2019 def addTrackSelection(
2020     s: acts.examples.Sequencer,
2021     trackSelectorConfig: TrackSelectorConfig,
2022     inputTracks: str,
2023     outputTracks: str,
2024     logLevel: Optional[acts.logging.Level] = None,
2025 ) -> acts.examples.TrackSelectorAlgorithm:
2026     customLogLevel = acts.examples.defaultLogging(s, logLevel)
2027 
2028     # single cut config for implicit single bin eta configuration
2029     selectorConfig = acts.TrackSelector.Config(
2030         **trackSelectorDefaultKWArgs(trackSelectorConfig)
2031     )
2032 
2033     trackSelector = acts.examples.TrackSelectorAlgorithm(
2034         level=customLogLevel(),
2035         inputTracks=inputTracks,
2036         outputTracks=outputTracks,
2037         selectorConfig=selectorConfig,
2038     )
2039 
2040     s.addAlgorithm(trackSelector)
2041 
2042     return trackSelector
2043 
2044 
2045 GnnBackend = Enum("GnnBackend", "Torch Onnx")
2046 
2047 
2048 def addGnn(
2049     s: acts.examples.Sequencer,
2050     graphConstructor,
2051     edgeClassifiers: list,
2052     trackBuilder,
2053     nodeFeatures: list,
2054     featureScales: list,
2055     inputSpacePoints: str = "spacepoints",
2056     inputClusters: str = "",
2057     outputDirRoot: Optional[Union[Path, str]] = None,
2058     logLevel: Optional[acts.logging.Level] = None,
2059 ) -> acts.examples.Sequencer:
2060     """
2061     Add GNN track finding with custom stage implementations.
2062 
2063     This is a flexible low-level API that accepts pre-configured GNN stage components.
2064     For examples of how to configure stages, see gnn_metric_learning.py and gnn_module_map.py.
2065 
2066     Args:
2067         s: Sequencer to add algorithms to
2068         graphConstructor: Graph construction stage (TorchMetricLearning, ModuleMapCuda, etc.)
2069         edgeClassifiers: List of edge classification stages (run sequentially)
2070         trackBuilder: Track building stage (BoostTrackBuilding, CudaTrackBuilding, etc.)
2071         nodeFeatures: List of node features to extract from spacepoints/clusters
2072         featureScales: Scaling factors for each feature
2073         trackingGeometry: Optional tracking geometry for creating spacepoints
2074         geometrySelection: Optional geometry selection file for spacepoint creation
2075         inputSpacePoints: Name of input spacepoint collection (default: "spacepoints")
2076         inputClusters: Name of input cluster collection (default: "")
2077         outputDirRoot: Optional output directory for performance ROOT files
2078         logLevel: Logging level
2079 
2080     Note:
2081         The trackingGeometry parameter serves two distinct purposes depending on the workflow:
2082         1. Spacepoint creation: When provided along with geometrySelection, creates spacepoints
2083            from measurements using SpacePointMaker (typical for simulation workflows)
2084         2. Module map usage: Some graph constructors (e.g., ModuleMapCuda) require
2085            trackingGeometry to map module IDs even when using pre-existing spacepoints
2086     """
2087     customLogLevel = acts.examples.defaultLogging(s, logLevel)
2088 
2089     # Validate that nodeFeatures and featureScales have matching lengths
2090     if len(nodeFeatures) != len(featureScales):
2091         raise ValueError(
2092             f"nodeFeatures and featureScales must have the same length "
2093             f"(got {len(nodeFeatures)} and {len(featureScales)})"
2094         )
2095 
2096     # GNN track finding algorithm
2097     findingAlg = acts.examples.gnn.TrackFindingAlgorithmGnn(
2098         level=customLogLevel(),
2099         inputSpacePoints=inputSpacePoints,
2100         inputClusters=inputClusters,
2101         outputProtoTracks="gnn_prototracks",
2102         graphConstructor=graphConstructor,
2103         edgeClassifiers=edgeClassifiers,
2104         trackBuilder=trackBuilder,
2105         nodeFeatures=nodeFeatures,
2106         featureScales=featureScales,
2107     )
2108     s.addAlgorithm(findingAlg)
2109     s.addWhiteboardAlias("prototracks", findingAlg.config.outputProtoTracks)
2110 
2111     # Convert prototracks to tracks
2112     s.addAlgorithm(
2113         acts.examples.PrototracksToTracks(
2114             level=customLogLevel(),
2115             inputProtoTracks="prototracks",
2116             inputMeasurements="measurements",
2117             outputTracks="tracks",
2118         )
2119     )
2120 
2121     # Truth matching
2122     matchAlg = acts.examples.TrackTruthMatcher(
2123         level=customLogLevel(),
2124         inputTracks="tracks",
2125         inputParticles="particles",
2126         inputMeasurementParticlesMap="measurement_particles_map",
2127         outputTrackParticleMatching="gnn_track_particle_matching",
2128         outputParticleTrackMatching="gnn_particle_track_matching",
2129         doubleMatching=True,
2130     )
2131     s.addAlgorithm(matchAlg)
2132     s.addWhiteboardAlias(
2133         "track_particle_matching", matchAlg.config.outputTrackParticleMatching
2134     )
2135     s.addWhiteboardAlias(
2136         "particle_track_matching", matchAlg.config.outputParticleTrackMatching
2137     )
2138 
2139     # Optional performance writer
2140     if outputDirRoot is not None:
2141         s.addWriter(
2142             acts.examples.root.RootTrackFinderNTupleWriter(
2143                 level=customLogLevel(),
2144                 inputTracks="tracks",
2145                 inputParticles="particles",
2146                 inputParticleMeasurementsMap="particle_measurements_map",
2147                 inputTrackParticleMatching=matchAlg.config.outputTrackParticleMatching,
2148                 filePath=str(Path(outputDirRoot) / "performance_track_finding.root"),
2149             )
2150         )
2151 
2152     return s
2153 
2154 
2155 @acts.examples.NamedTypeArgs(
2156     config=AmbiguityResolutionConfig,
2157 )
2158 def addAmbiguityResolution(
2159     s,
2160     config: AmbiguityResolutionConfig = AmbiguityResolutionConfig(),
2161     tracks: str = "tracks",
2162     outputDirCsv: Optional[Union[Path, str]] = None,
2163     outputDirRoot: Optional[Union[Path, str]] = None,
2164     writeTrackSummary: bool = True,
2165     writeTrackStates: bool = False,
2166     writePerformance: bool = True,
2167     writeCovMat=False,
2168     logLevel: Optional[acts.logging.Level] = None,
2169 ) -> None:
2170     from acts.examples import GreedyAmbiguityResolutionAlgorithm
2171 
2172     customLogLevel = acts.examples.defaultLogging(s, logLevel)
2173 
2174     alg = GreedyAmbiguityResolutionAlgorithm(
2175         level=customLogLevel(),
2176         inputTracks=tracks,
2177         outputTracks="ambi_tracks",
2178         **acts.examples.defaultKWArgs(
2179             maximumSharedHits=config.maximumSharedHits,
2180             nMeasurementsMin=config.nMeasurementsMin,
2181             maximumIterations=config.maximumIterations,
2182         ),
2183     )
2184     s.addAlgorithm(alg)
2185     s.addWhiteboardAlias("tracks", alg.config.outputTracks)
2186 
2187     matchAlg = acts.examples.TrackTruthMatcher(
2188         level=customLogLevel(),
2189         inputTracks=alg.config.outputTracks,
2190         inputParticles="particles",
2191         inputMeasurementParticlesMap="measurement_particles_map",
2192         outputTrackParticleMatching="ambi_track_particle_matching",
2193         outputParticleTrackMatching="ambi_particle_track_matching",
2194         doubleMatching=True,
2195     )
2196     s.addAlgorithm(matchAlg)
2197     s.addWhiteboardAlias(
2198         "track_particle_matching", matchAlg.config.outputTrackParticleMatching
2199     )
2200     s.addWhiteboardAlias(
2201         "particle_track_matching", matchAlg.config.outputParticleTrackMatching
2202     )
2203 
2204     addTrackWriters(
2205         s,
2206         name="ambi",
2207         tracks=alg.config.outputTracks,
2208         outputDirCsv=outputDirCsv,
2209         outputDirRoot=outputDirRoot,
2210         writeSummary=writeTrackSummary,
2211         writeStates=writeTrackStates,
2212         writeFitterPerformance=writePerformance,
2213         writeFinderPerformance=writePerformance,
2214         writeCovMat=writeCovMat,
2215         logLevel=logLevel,
2216     )
2217 
2218     return s
2219 
2220 
2221 @acts.examples.NamedTypeArgs(
2222     config=ScoreBasedAmbiguityResolutionConfig,
2223 )
2224 def addScoreBasedAmbiguityResolution(
2225     s,
2226     config: ScoreBasedAmbiguityResolutionConfig = ScoreBasedAmbiguityResolutionConfig(),
2227     tracks: str = "tracks",
2228     outputDirCsv: Optional[Union[Path, str]] = None,
2229     outputDirRoot: Optional[Union[Path, str]] = None,
2230     ambiVolumeFile: Optional[Union[Path, str]] = None,
2231     writeTrackSummary: bool = True,
2232     writeTrackStates: bool = False,
2233     writePerformance: bool = True,
2234     writeCovMat=False,
2235     logLevel: Optional[acts.logging.Level] = None,
2236 ) -> None:
2237     from acts.examples import ScoreBasedAmbiguityResolutionAlgorithm
2238 
2239     customLogLevel = acts.examples.defaultLogging(s, acts.logging.INFO)
2240 
2241     algScoreBased = ScoreBasedAmbiguityResolutionAlgorithm(
2242         level=customLogLevel(),
2243         inputTracks=tracks,
2244         configFile=ambiVolumeFile,
2245         outputTracks="ambiTracksScoreBased",
2246         **acts.examples.defaultKWArgs(
2247             minScore=config.minScore,
2248             minScoreSharedTracks=config.minScoreSharedTracks,
2249             maxShared=config.maxShared,
2250             minUnshared=config.minUnshared,
2251             maxSharedTracksPerMeasurement=config.maxSharedTracksPerMeasurement,
2252             useAmbiguityScoring=config.useAmbiguityScoring,
2253         ),
2254     )
2255     s.addAlgorithm(algScoreBased)
2256     s.addWhiteboardAlias("tracks", algScoreBased.config.outputTracks)
2257 
2258     matchAlg = acts.examples.TrackTruthMatcher(
2259         level=customLogLevel(),
2260         inputTracks=algScoreBased.config.outputTracks,
2261         inputParticles="particles",
2262         inputMeasurementParticlesMap="measurement_particles_map",
2263         outputTrackParticleMatching="ambi_scorebased_track_particle_matching",
2264         outputParticleTrackMatching="ambi_scorebased_particle_track_matching",
2265         doubleMatching=True,
2266     )
2267     s.addAlgorithm(matchAlg)
2268     s.addWhiteboardAlias(
2269         "track_particle_matching", matchAlg.config.outputTrackParticleMatching
2270     )
2271     s.addWhiteboardAlias(
2272         "particle_track_matching", matchAlg.config.outputParticleTrackMatching
2273     )
2274 
2275     addTrackWriters(
2276         s,
2277         name="ambi_scorebased",
2278         tracks=algScoreBased.config.outputTracks,
2279         outputDirCsv=outputDirCsv,
2280         outputDirRoot=outputDirRoot,
2281         writeSummary=writeTrackSummary,
2282         writeStates=writeTrackStates,
2283         writeFitterPerformance=writePerformance,
2284         writeFinderPerformance=writePerformance,
2285         writeCovMat=writeCovMat,
2286         logLevel=logLevel,
2287     )
2288 
2289     return s
2290 
2291 
2292 @acts.examples.NamedTypeArgs(
2293     config=AmbiguityResolutionMLConfig,
2294 )
2295 def addAmbiguityResolutionML(
2296     s,
2297     config: AmbiguityResolutionMLConfig = AmbiguityResolutionMLConfig(),
2298     tracks: str = "tracks",
2299     onnxModelFile: Optional[Union[Path, str]] = None,
2300     outputDirCsv: Optional[Union[Path, str]] = None,
2301     outputDirRoot: Optional[Union[Path, str]] = None,
2302     writeTrackSummary: bool = True,
2303     writeTrackStates: bool = False,
2304     writePerformance: bool = True,
2305     writeCovMat=False,
2306     logLevel: Optional[acts.logging.Level] = None,
2307 ) -> None:
2308     from acts.examples.onnx import AmbiguityResolutionMLAlgorithm
2309     from acts.examples import GreedyAmbiguityResolutionAlgorithm
2310 
2311     customLogLevel = acts.examples.defaultLogging(s, logLevel)
2312     algML = AmbiguityResolutionMLAlgorithm(
2313         level=customLogLevel(),
2314         inputTracks=tracks,
2315         inputDuplicateNN=onnxModelFile,
2316         outputTracks="ambiTracksML",
2317         **acts.examples.defaultKWArgs(
2318             nMeasurementsMin=config.nMeasurementsMin,
2319         ),
2320     )
2321 
2322     algGreedy = GreedyAmbiguityResolutionAlgorithm(
2323         level=customLogLevel(),
2324         inputTracks=algML.config.outputTracks,
2325         outputTracks="ambiTracksMLGreedy",
2326         **acts.examples.defaultKWArgs(
2327             maximumSharedHits=config.maximumSharedHits,
2328             nMeasurementsMin=config.nMeasurementsMin,
2329             maximumIterations=config.maximumIterations,
2330         ),
2331     )
2332 
2333     s.addAlgorithm(algML)
2334     s.addAlgorithm(algGreedy)
2335 
2336     s.addWhiteboardAlias("tracks", algGreedy.config.outputTracks)
2337 
2338     matchAlg = acts.examples.TrackTruthMatcher(
2339         level=customLogLevel(),
2340         inputTracks=algGreedy.config.outputTracks,
2341         inputParticles="particles",
2342         inputMeasurementParticlesMap="measurement_particles_map",
2343         outputTrackParticleMatching="ambiML_track_particle_matching",
2344         outputParticleTrackMatching="ambiML_particle_track_matching",
2345         doubleMatching=True,
2346     )
2347     s.addAlgorithm(matchAlg)
2348     s.addWhiteboardAlias(
2349         "track_particle_matching", matchAlg.config.outputTrackParticleMatching
2350     )
2351     s.addWhiteboardAlias(
2352         "particle_track_matching", matchAlg.config.outputParticleTrackMatching
2353     )
2354 
2355     addTrackWriters(
2356         s,
2357         name="ambiML",
2358         tracks=algGreedy.config.outputTracks,
2359         outputDirCsv=outputDirCsv,
2360         outputDirRoot=outputDirRoot,
2361         writeSummary=writeTrackSummary,
2362         writeStates=writeTrackStates,
2363         writeFitterPerformance=writePerformance,
2364         writeFinderPerformance=writePerformance,
2365         writeCovMat=writeCovMat,
2366         logLevel=logLevel,
2367     )
2368 
2369     return s
2370 
2371 
2372 @acts.examples.NamedTypeArgs(
2373     trackSelectorConfig=TrackSelectorConfig,
2374 )
2375 def addVertexFitting(
2376     s,
2377     field,
2378     tracks: Optional[str] = "tracks",
2379     trackParameters: Optional[str] = None,
2380     outputProtoVertices: str = "protovertices",
2381     outputVertices: str = "fittedVertices",
2382     vertexFinder: VertexFinder = VertexFinder.Truth,
2383     maxIterations: Optional[int] = None,
2384     initialVariances: Optional[List[float]] = None,
2385     useTime: Optional[bool] = False,
2386     seeder: Optional[
2387         acts.examples.VertexSeedFinder
2388     ] = acts.examples.VertexSeedFinder.GaussianSeeder,
2389     spatialBinExtent: Optional[float] = None,
2390     temporalBinExtent: Optional[float] = None,
2391     simultaneousSeeds: Optional[int] = None,
2392     trackSelectorConfig: Optional[TrackSelectorConfig] = None,
2393     writeTrackInfo: bool = False,
2394     outputDirRoot: Optional[Union[Path, str]] = None,
2395     outputDirCsv: Optional[Union[Path, str]] = None,
2396     logLevel: Optional[acts.logging.Level] = None,
2397 ) -> None:
2398     """This function steers the vertex fitting
2399 
2400     Parameters
2401     ----------
2402     s: Sequencer
2403         the sequencer module to which we add the Seeding steps (returned from
2404         addVertexFitting)
2405     field : magnetic field
2406     outputDirRoot : Path|str, path, None
2407         the output folder for ROOT output, None triggers no output
2408     outputDirCsv : Path|str, path, None
2409         the output folder for the CSV output, None triggers no output
2410     vertexFinder : VertexFinder, Truth
2411         vertexFinder algorithm: one of Truth, AMVF, Iterative
2412     seeder : enum member
2413         determines vertex seeder for AMVF, can be acts.seeder.GaussianSeeder or
2414         acts.seeder.AdaptiveGridSeeder
2415     useTime : bool, False
2416         determines whether time information is used in vertex seeder, finder,
2417         and fitter
2418         only implemented for the AMVF and the AdaptiveGridSeeder
2419     spatialBinExtent : float, None
2420         spatial bin extent for the AdaptiveGridSeeder
2421     temporalBinExtent : float, None
2422         temporal bin extent for the AdaptiveGridSeeder
2423     logLevel : acts.logging.Level, None
2424         logging level to override setting given in `s`
2425     """
2426     from acts.examples import (
2427         TruthVertexFinder,
2428         VertexFitterAlgorithm,
2429         IterativeVertexFinderAlgorithm,
2430         AdaptiveMultiVertexFinderAlgorithm,
2431         CsvVertexWriter,
2432     )
2433     from acts.examples.root import RootVertexNTupleWriter
2434 
2435     customLogLevel = acts.examples.defaultLogging(s, logLevel)
2436 
2437     if tracks is not None and trackSelectorConfig is not None:
2438         trackSelector = addTrackSelection(
2439             s,
2440             trackSelectorConfig,
2441             inputTracks=tracks,
2442             outputTracks="selectedTracksVertexing",
2443             logLevel=customLogLevel(),
2444         )
2445         tracks = trackSelector.config.outputTracks
2446 
2447     if trackParameters is None:
2448         converter = acts.examples.TracksToParameters(
2449             level=customLogLevel(),
2450             inputTracks=tracks,
2451             outputTrackParameters="selectedTracksParametersVertexing",
2452         )
2453         s.addAlgorithm(converter)
2454         trackParameters = converter.config.outputTrackParameters
2455 
2456     tracks = tracks if tracks is not None else ""
2457     inputParticles = "particles"
2458     selectedParticles = "particles_selected"
2459     inputTruthVertices = "vertices_truth"
2460 
2461     if vertexFinder == VertexFinder.Truth:
2462         findVertices = TruthVertexFinder(
2463             level=customLogLevel(),
2464             inputTracks=tracks,
2465             inputTrackParticleMatching="track_particle_matching",
2466             outputProtoVertices=outputProtoVertices,
2467             excludeSecondaries=True,
2468         )
2469         s.addAlgorithm(findVertices)
2470         fitVertices = VertexFitterAlgorithm(
2471             level=customLogLevel(),
2472             inputTrackParameters=trackParameters,
2473             inputProtoVertices=findVertices.config.outputProtoVertices,
2474             outputVertices=outputVertices,
2475             bField=field,
2476         )
2477         s.addAlgorithm(fitVertices)
2478     elif vertexFinder == VertexFinder.Iterative:
2479         findVertices = IterativeVertexFinderAlgorithm(
2480             level=customLogLevel(),
2481             inputTrackParameters=trackParameters,
2482             outputProtoVertices=outputProtoVertices,
2483             outputVertices=outputVertices,
2484             bField=field,
2485         )
2486         s.addAlgorithm(findVertices)
2487     elif vertexFinder == VertexFinder.AMVF:
2488         findVertices = AdaptiveMultiVertexFinderAlgorithm(
2489             level=customLogLevel(),
2490             inputTrackParameters=trackParameters,
2491             inputTruthParticles=selectedParticles,
2492             inputTruthVertices=inputTruthVertices,
2493             outputProtoVertices=outputProtoVertices,
2494             outputVertices=outputVertices,
2495             bField=field,
2496             seedFinder=seeder,
2497             **acts.examples.defaultKWArgs(
2498                 maxIterations=maxIterations,
2499                 initialVariances=initialVariances,
2500                 useTime=useTime,
2501                 spatialBinExtent=spatialBinExtent,
2502                 temporalBinExtent=temporalBinExtent,
2503                 simultaneousSeeds=simultaneousSeeds,
2504             ),
2505         )
2506         s.addAlgorithm(findVertices)
2507     else:
2508         raise RuntimeError("Invalid finder argument")
2509 
2510     if outputDirCsv is not None:
2511         outputDirCsv = Path(outputDirCsv)
2512         if not outputDirCsv.exists():
2513             outputDirCsv.mkdir()
2514         s.addWriter(
2515             CsvVertexWriter(
2516                 level=customLogLevel(),
2517                 inputVertices=outputVertices,
2518                 outputDir=str(outputDirCsv),
2519                 outputStem="vertices",
2520             )
2521         )
2522 
2523     if outputDirRoot is not None:
2524         outputDirRoot = Path(outputDirRoot)
2525         if not outputDirRoot.exists():
2526             outputDirRoot.mkdir()
2527         s.addWriter(
2528             RootVertexNTupleWriter(
2529                 level=customLogLevel(),
2530                 inputVertices=outputVertices,
2531                 inputTracks=tracks,
2532                 inputTruthVertices=inputTruthVertices,
2533                 inputParticles=inputParticles,
2534                 inputSelectedParticles=selectedParticles,
2535                 inputTrackParticleMatching="track_particle_matching",
2536                 bField=field,
2537                 writeTrackInfo=writeTrackInfo,
2538                 treeName="vertexing",
2539                 filePath=str(outputDirRoot / "performance_vertexing.root"),
2540             )
2541         )
2542 
2543     return s
2544 
2545 
2546 def addHoughVertexFinding(
2547     s,
2548     outputDirRoot: Optional[Union[Path, str]] = None,
2549     logLevel: Optional[acts.logging.Level] = None,
2550     inputSpacePoints: Optional[str] = "spacepoints",
2551     outputVertices: Optional[str] = "fittedHoughVertices",
2552 ) -> None:
2553     from acts.examples import (
2554         HoughVertexFinderAlgorithm,
2555         RootVertexNTupleWriter,
2556     )
2557 
2558     customLogLevel = acts.examples.defaultLogging(s, logLevel)
2559 
2560     findHoughVertex = HoughVertexFinderAlgorithm(
2561         level=customLogLevel(),
2562         inputSpacepoints=inputSpacePoints,
2563         outputVertices=outputVertices,
2564     )
2565     s.addAlgorithm(findHoughVertex)
2566 
2567     inputParticles = "particles"
2568     selectedParticles = "particles_selected"
2569     inputTruthVertices = "vertices_truth"
2570 
2571     if outputDirRoot is not None:
2572         outputDirRoot = Path(outputDirRoot)
2573         if not outputDirRoot.exists():
2574             outputDirRoot.mkdir()
2575 
2576         s.addWriter(
2577             RootVertexNTupleWriter(
2578                 level=customLogLevel(),
2579                 inputParticles=inputParticles,
2580                 inputSelectedParticles=selectedParticles,
2581                 inputTracks="",
2582                 inputTrackParticleMatching="",
2583                 writeTrackInfo=False,
2584                 inputTruthVertices=inputTruthVertices,
2585                 inputVertices=outputVertices,
2586                 treeName="houghvertexing",
2587                 filePath=str(outputDirRoot / "performance_houghvertexing.root"),
2588             )
2589         )
2590 
2591     return s