Back to home page

EIC code displayed by LXR

 
 

    


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

0001 from typing import Optional, Union, Any, List
0002 from pathlib import Path
0003 from collections import namedtuple
0004 from collections.abc import Iterable
0005 
0006 import acts
0007 import acts.examples
0008 from acts.examples import (
0009     RandomNumbers,
0010     EventGenerator,
0011     FixedMultiplicityGenerator,
0012     CsvParticleWriter,
0013     ParticlesPrinter,
0014     CsvVertexWriter,
0015 )
0016 
0017 from acts.examples.root import (
0018     RootParticleWriter,
0019     RootVertexWriter,
0020     RootSimHitWriter,
0021 )
0022 
0023 import acts.examples.hepmc3
0024 
0025 # Defaults (given as `None` here) use class defaults defined in
0026 # Examples/Algorithms/Generators/ActsExamples/Generators/ParametricParticleGenerator.hpp
0027 MomentumConfig = namedtuple(
0028     "MomentumConfig",
0029     ["min", "max", "transverse", "logUniform"],
0030     defaults=[None, None, None, None],
0031 )
0032 EtaConfig = namedtuple(
0033     "EtaConfig", ["min", "max", "uniform"], defaults=[None, None, None]
0034 )
0035 PhiConfig = namedtuple("PhiConfig", ["min", "max"], defaults=[None, None])
0036 ParticleConfig = namedtuple(
0037     "ParticleConfig",
0038     ["num", "pdg", "randomizeCharge", "charge", "mass"],
0039     defaults=[None, None, None, None, None],
0040 )
0041 ParticleSelectorConfig = namedtuple(
0042     "ParticleSelectorConfig",
0043     [
0044         "rho",  # (min,max)
0045         "absZ",  # (min,max)
0046         "time",  # (min,max)
0047         "phi",  # (min,max)
0048         "eta",  # (min,max)
0049         "absEta",  # (min,max)
0050         "pt",  # (min,max)
0051         "m",  # (min,max)
0052         "hits",  # (min,max)
0053         "measurements",  # (min,max)
0054         "removeCharged",  # bool
0055         "removeNeutral",  # bool
0056         "removeSecondaries",  # bool
0057         "nMeasurementsGroupMin",
0058     ],
0059     defaults=[(None, None)] * 10 + [None] * 4,
0060 )
0061 
0062 TruthJetConfig = namedtuple(
0063     "TruthJetConfig",
0064     ["inputTruthParticles", "outputJets", "jetPtMin"],
0065     defaults=[None, None],
0066 )
0067 
0068 
0069 def _getParticleSelectionKWargs(config: ParticleSelectorConfig) -> dict:
0070     return {
0071         "rhoMin": config.rho[0],
0072         "rhoMax": config.rho[1],
0073         "absZMin": config.absZ[0],
0074         "absZMax": config.absZ[1],
0075         "timeMin": config.time[0],
0076         "timeMax": config.time[1],
0077         "phiMin": config.phi[0],
0078         "phiMax": config.phi[1],
0079         "etaMin": config.eta[0],
0080         "etaMax": config.eta[1],
0081         "absEtaMin": config.absEta[0],
0082         "absEtaMax": config.absEta[1],
0083         "ptMin": config.pt[0],
0084         "ptMax": config.pt[1],
0085         "mMin": config.m[0],
0086         "mMax": config.m[1],
0087         "hitsMin": config.hits[0],
0088         "hitsMax": config.hits[1],
0089         "measurementsMin": config.measurements[0],
0090         "measurementsMax": config.measurements[1],
0091         "removeCharged": config.removeCharged,
0092         "removeNeutral": config.removeNeutral,
0093         "removeSecondaries": config.removeSecondaries,
0094         "measurementCounter": config.nMeasurementsGroupMin,
0095     }
0096 
0097 
0098 def _getTruthJetKWargs(config: TruthJetConfig) -> dict:
0099     return {
0100         "inputTruthParticles": config.inputTruthParticles,
0101         "outputJets": config.outputJets,
0102         "jetPtMin": config.jetPtMin,
0103     }
0104 
0105 
0106 @acts.examples.NamedTypeArgs(
0107     momentumConfig=MomentumConfig,
0108     etaConfig=EtaConfig,
0109     phiConfig=PhiConfig,
0110     particleConfig=ParticleConfig,
0111 )
0112 def addParticleGun(
0113     s: acts.examples.Sequencer,
0114     outputDirCsv: Optional[Union[Path, str]] = None,
0115     outputDirRoot: Optional[Union[Path, str]] = None,
0116     momentumConfig: MomentumConfig = MomentumConfig(),
0117     etaConfig: EtaConfig = EtaConfig(),
0118     phiConfig: PhiConfig = PhiConfig(),
0119     particleConfig: ParticleConfig = ParticleConfig(),
0120     multiplicity: int = 1,
0121     vtxGen: Optional[EventGenerator.VertexGenerator] = None,
0122     printParticles: bool = False,
0123     rnd: Optional[RandomNumbers] = None,
0124     logLevel: Optional[acts.logging.Level] = None,
0125 ) -> None:
0126     """This function steers the particle generation using the particle gun
0127 
0128     Parameters
0129     ----------
0130     s: Sequencer
0131         the sequencer module to which we add the particle gun steps (returned from addParticleGun)
0132     outputDirCsv : Path|str, path, None
0133         the output folder for the Csv output, None triggers no output
0134     outputDirRoot : Path|str, path, None
0135         the output folder for the Root output, None triggers no output
0136     momentumConfig : MomentumConfig(min, max, transverse, logUniform)
0137         momentum configuration: minimum momentum, maximum momentum, transverse, log-uniform
0138     etaConfig : EtaConfig(min, max, uniform)
0139         pseudorapidity configuration: eta min, eta max, uniform
0140     phiConfig : PhiConfig(min, max)
0141         azimuthal angle configuration: phi min, phi max
0142     particleConfig : ParticleConfig(num, pdg, randomizeCharge, charge, mass)
0143         particle configuration: number of particles, particle type, charge flip
0144     multiplicity : int, 1
0145         number of generated vertices
0146     vtxGen : VertexGenerator, None
0147         vertex generator module
0148     printParticles : bool, False
0149         print generated particles
0150     rnd : RandomNumbers, None
0151         random number generator
0152     """
0153 
0154     customLogLevel = acts.examples.defaultLogging(s, logLevel)
0155 
0156     rnd = rnd or RandomNumbers(seed=228)
0157 
0158     evGen = EventGenerator(
0159         level=customLogLevel(),
0160         generators=[
0161             EventGenerator.Generator(
0162                 multiplicity=FixedMultiplicityGenerator(n=multiplicity),
0163                 vertex=vtxGen
0164                 or acts.examples.GaussianVertexGenerator(
0165                     mean=acts.Vector4(0, 0, 0, 0),
0166                     stddev=acts.Vector4(0, 0, 0, 0),
0167                 ),
0168                 particles=acts.examples.ParametricParticleGenerator(
0169                     **acts.examples.defaultKWArgs(
0170                         p=(momentumConfig.min, momentumConfig.max),
0171                         pTransverse=momentumConfig.transverse,
0172                         pLogUniform=momentumConfig.logUniform,
0173                         eta=(etaConfig.min, etaConfig.max),
0174                         phi=(phiConfig.min, phiConfig.max),
0175                         etaUniform=etaConfig.uniform,
0176                         numParticles=particleConfig.num,
0177                         pdg=particleConfig.pdg,
0178                         randomizeCharge=particleConfig.randomizeCharge,
0179                         charge=particleConfig.charge,
0180                         mass=particleConfig.mass,
0181                         # Merging particle gun vertices does not make sense
0182                     )
0183                 ),
0184             )
0185         ],
0186         randomNumbers=rnd,
0187         outputEvent="particle_gun_event",
0188     )
0189     s.addReader(evGen)
0190 
0191     hepmc3Converter = acts.examples.hepmc3.HepMC3InputConverter(
0192         level=customLogLevel(),
0193         inputEvent=evGen.config.outputEvent,
0194         outputParticles="particles_generated",
0195         outputVertices="vertices_generated",
0196         mergePrimaries=False,
0197     )
0198     s.addAlgorithm(hepmc3Converter)
0199 
0200     s.addWhiteboardAlias("particles", hepmc3Converter.config.outputParticles)
0201     s.addWhiteboardAlias("vertices_truth", hepmc3Converter.config.outputVertices)
0202 
0203     s.addWhiteboardAlias(
0204         "particles_generated_selected", hepmc3Converter.config.outputParticles
0205     )
0206 
0207     if printParticles:
0208         s.addAlgorithm(
0209             ParticlesPrinter(
0210                 level=customLogLevel(),
0211                 inputParticles=hepmc3Converter.config.outputParticles,
0212             )
0213         )
0214 
0215     if outputDirCsv is not None:
0216         outputDirCsv = Path(outputDirCsv)
0217         if not outputDirCsv.exists():
0218             outputDirCsv.mkdir()
0219 
0220         s.addWriter(
0221             CsvParticleWriter(
0222                 level=customLogLevel(),
0223                 inputParticles=hepmc3Converter.config.outputParticles,
0224                 outputDir=str(outputDirCsv),
0225                 outputStem="particles",
0226             )
0227         )
0228 
0229     if outputDirRoot is not None:
0230         outputDirRoot = Path(outputDirRoot)
0231         if not outputDirRoot.exists():
0232             outputDirRoot.mkdir()
0233 
0234         s.addWriter(
0235             RootParticleWriter(
0236                 level=customLogLevel(),
0237                 inputParticles=hepmc3Converter.config.outputParticles,
0238                 filePath=str(outputDirRoot / "particles.root"),
0239             )
0240         )
0241 
0242         s.addWriter(
0243             RootVertexWriter(
0244                 level=customLogLevel(),
0245                 inputVertices=hepmc3Converter.config.outputVertices,
0246                 filePath=str(outputDirRoot / "vertices.root"),
0247             )
0248         )
0249 
0250     return s
0251 
0252 
0253 def addPythia8(
0254     s: acts.examples.Sequencer,
0255     rnd: Optional[acts.examples.RandomNumbers] = None,
0256     nhard: int = 1,
0257     npileup: int = 200,
0258     beam: Optional[
0259         Union[acts.PdgParticle, Iterable]
0260     ] = None,  # default: acts.PdgParticle.eProton
0261     cmsEnergy: Optional[float] = None,  # default: 14 * acts.UnitConstants.TeV
0262     hardProcess: Optional[Iterable] = None,  # default: ["HardQCD:all = on"]
0263     pileupProcess: Iterable = ["SoftQCD:all = on"],
0264     vtxGen: Optional[EventGenerator.VertexGenerator] = None,
0265     outputDirCsv: Optional[Union[Path, str]] = None,
0266     outputDirRoot: Optional[Union[Path, str]] = None,
0267     printParticles: bool = False,
0268     printPythiaEventListing: Optional[Union[None, str]] = None,
0269     writeHepMC3: Optional[Path] = None,
0270     printListing: bool = False,
0271     logLevel: Optional[acts.logging.Level] = None,
0272 ) -> None:
0273     """This function steers the particle generation using Pythia8
0274 
0275     Parameters
0276     ----------
0277     s: Sequencer
0278         the sequencer module to which we add the particle gun steps (returned from addParticleGun)
0279     rnd : RandomNumbers, None
0280         random number generator
0281     nhard, npileup : int, 1, 200
0282         Number of hard-scatter and pileup vertices
0283     beam : PdgParticle|[PdgParticle,PdgParticle], eProton
0284         beam particle(s)
0285     cmsEnergy : float, 14 TeV
0286         CMS energy
0287     hardProcess, pileupProcess : [str], ["HardQCD:all = on"], ["SoftQCD:all = on"]
0288         hard and pileup processes
0289     vtxGen : VertexGenerator, None
0290         vertex generator module
0291     outputDirCsv : Path|str, path, None
0292         the output folder for the Csv output, None triggers no output
0293     outputDirRoot : Path|str, path, None
0294         the output folder for the Root output, None triggers no output
0295     printParticles : bool, False
0296         print generated particles
0297     writeHepMC3 : Path|None
0298         write directly from Pythia8 into HepMC3
0299     printPythiaEventListing
0300         None or "short" or "long"
0301     """
0302 
0303     import acts
0304 
0305     customLogLevel = acts.examples.defaultLogging(s, logLevel)
0306 
0307     rnd = rnd or acts.examples.RandomNumbers()
0308 
0309     vtxGen = vtxGen or acts.examples.GaussianVertexGenerator(
0310         stddev=acts.Vector4(0, 0, 0, 0), mean=acts.Vector4(0, 0, 0, 0)
0311     )
0312 
0313     if not isinstance(beam, Iterable):
0314         beam = (beam, beam)
0315 
0316     if printPythiaEventListing is None:
0317         printShortEventListing = False
0318         printLongEventListing = False
0319     elif printPythiaEventListing == "short":
0320         printShortEventListing = True
0321         printLongEventListing = False
0322     elif printPythiaEventListing == "long":
0323         printShortEventListing = False
0324         printLongEventListing = True
0325     else:
0326         raise RuntimeError("Invalid pythia config")
0327 
0328     generators = []
0329     if nhard is not None and nhard > 0:
0330         import acts.examples.pythia8
0331 
0332         generators.append(
0333             acts.examples.EventGenerator.Generator(
0334                 multiplicity=acts.examples.FixedMultiplicityGenerator(n=nhard),
0335                 vertex=vtxGen,
0336                 particles=acts.examples.pythia8.Pythia8Generator(
0337                     level=customLogLevel(),
0338                     **acts.examples.defaultKWArgs(
0339                         pdgBeam0=beam[0],
0340                         pdgBeam1=beam[1],
0341                         cmsEnergy=cmsEnergy,
0342                         settings=hardProcess,
0343                         printLongEventListing=printLongEventListing,
0344                         printShortEventListing=printShortEventListing,
0345                         writeHepMC3=writeHepMC3,
0346                     ),
0347                 ),
0348             )
0349         )
0350     if npileup > 0:
0351         import acts.examples.pythia8
0352 
0353         generators.append(
0354             acts.examples.EventGenerator.Generator(
0355                 multiplicity=acts.examples.FixedMultiplicityGenerator(n=npileup),
0356                 vertex=vtxGen,
0357                 particles=acts.examples.pythia8.Pythia8Generator(
0358                     level=customLogLevel(),
0359                     **acts.examples.defaultKWArgs(
0360                         pdgBeam0=beam[0],
0361                         pdgBeam1=beam[1],
0362                         cmsEnergy=cmsEnergy,
0363                         settings=pileupProcess,
0364                     ),
0365                 ),
0366             )
0367         )
0368 
0369     evGen = acts.examples.EventGenerator(
0370         level=customLogLevel(),
0371         generators=generators,
0372         randomNumbers=rnd,
0373         outputEvent="pythia8-event",
0374         printListing=printListing,
0375     )
0376     s.addReader(evGen)
0377 
0378     hepmc3Converter = acts.examples.hepmc3.HepMC3InputConverter(
0379         level=customLogLevel(),
0380         inputEvent=evGen.config.outputEvent,
0381         outputParticles="particles_generated",
0382         outputVertices="vertices_generated",
0383         printListing=printListing,
0384     )
0385     s.addAlgorithm(hepmc3Converter)
0386     s.addWhiteboardAlias("particles", hepmc3Converter.config.outputParticles)
0387     s.addWhiteboardAlias("vertices_truth", hepmc3Converter.config.outputVertices)
0388 
0389     s.addWhiteboardAlias(
0390         "particles_generated_selected", hepmc3Converter.config.outputParticles
0391     )
0392 
0393     if printParticles:
0394         s.addAlgorithm(
0395             acts.examples.ParticlesPrinter(
0396                 level=customLogLevel(),
0397                 inputParticles=hepmc3Converter.config.outputParticles,
0398             )
0399         )
0400 
0401     if outputDirCsv is not None:
0402         outputDirCsv = Path(outputDirCsv)
0403         if not outputDirCsv.exists():
0404             outputDirCsv.mkdir()
0405 
0406         s.addWriter(
0407             acts.examples.CsvParticleWriter(
0408                 level=customLogLevel(),
0409                 inputParticles=hepmc3Converter.config.outputParticles,
0410                 outputDir=str(outputDirCsv),
0411                 outputStem="particles",
0412             )
0413         )
0414 
0415     if outputDirRoot is not None:
0416         outputDirRoot = Path(outputDirRoot)
0417         if not outputDirRoot.exists():
0418             outputDirRoot.mkdir()
0419 
0420         s.addWriter(
0421             RootParticleWriter(
0422                 level=customLogLevel(),
0423                 inputParticles=hepmc3Converter.config.outputParticles,
0424                 filePath=str(outputDirRoot / "particles.root"),
0425             )
0426         )
0427 
0428         s.addWriter(
0429             RootVertexWriter(
0430                 level=customLogLevel(),
0431                 inputVertices=hepmc3Converter.config.outputVertices,
0432                 filePath=str(outputDirRoot / "vertices.root"),
0433             )
0434         )
0435 
0436     return s
0437 
0438 
0439 def addGenParticleSelection(
0440     s: acts.examples.Sequencer,
0441     config: ParticleSelectorConfig,
0442     logLevel: Optional[acts.logging.Level] = None,
0443 ) -> None:
0444     """
0445     This function steers the particle selection after generation.
0446 
0447     Parameters
0448     ----------
0449     s: Sequencer
0450         the sequencer module to which we add the ParticleSelector
0451     config: ParticleSelectorConfig
0452         the particle selection configuration
0453     """
0454     customLogLevel = acts.examples.defaultLogging(s, logLevel)
0455 
0456     selector = acts.examples.ParticleSelector(
0457         **acts.examples.defaultKWArgs(**_getParticleSelectionKWargs(config)),
0458         level=customLogLevel(),
0459         inputParticles="particles_generated",
0460         outputParticles="tmp_particles_generated_selected",
0461     )
0462     s.addAlgorithm(selector)
0463 
0464     s.addWhiteboardAlias("particles_selected", selector.config.outputParticles)
0465     s.addWhiteboardAlias(
0466         "particles_generated_selected", selector.config.outputParticles
0467     )
0468 
0469 
0470 def addFatras(
0471     s: acts.examples.Sequencer,
0472     trackingGeometry: acts.TrackingGeometry,
0473     field: acts.MagneticFieldProvider,
0474     rnd: acts.examples.RandomNumbers,
0475     enableInteractions: bool = True,
0476     pMin: Optional[float] = None,
0477     inputParticles: str = "particles_generated_selected",
0478     outputParticles: str = "particles_simulated",
0479     outputSimHits: str = "simhits",
0480     outputDirCsv: Optional[Union[Path, str]] = None,
0481     outputDirRoot: Optional[Union[Path, str]] = None,
0482     outputDirObj: Optional[Union[Path, str]] = None,
0483     logLevel: Optional[acts.logging.Level] = None,
0484 ) -> None:
0485     """This function steers the detector simulation using Fatras
0486 
0487     Parameters
0488     ----------
0489     s: Sequencer
0490         the sequencer module to which we add the Fatras steps (returned from addFatras)
0491     trackingGeometry : tracking geometry
0492     field : magnetic field
0493     rnd : RandomNumbers
0494         random number generator
0495     enableInteractions : Enable the particle interactions in the simulation
0496     pMin : Minimum monmentum of particles simulated by FATRAS
0497     outputDirCsv : Path|str, path, None
0498         the output folder for the Csv output, None triggers no output
0499     outputDirRoot : Path|str, path, None
0500         the output folder for the Root output, None triggers no output
0501     outputDirObj : Path|str, path, None
0502         the output folder for the Obj output, None triggers no output
0503     """
0504 
0505     customLogLevel = acts.examples.defaultLogging(s, logLevel)
0506 
0507     alg = acts.examples.FatrasSimulation(
0508         **acts.examples.defaultKWArgs(
0509             level=customLogLevel(),
0510             inputParticles=inputParticles,
0511             outputParticles=outputParticles,
0512             outputSimHits=outputSimHits,
0513             randomNumbers=rnd,
0514             trackingGeometry=trackingGeometry,
0515             magneticField=field,
0516             generateHitsOnSensitive=True,
0517             emScattering=enableInteractions,
0518             emEnergyLossIonisation=enableInteractions,
0519             emEnergyLossRadiation=enableInteractions,
0520             emPhotonConversion=enableInteractions,
0521             pMin=pMin,
0522         )
0523     )
0524     s.addAlgorithm(alg)
0525 
0526     s.addWhiteboardAlias("particles", outputParticles)
0527 
0528     s.addWhiteboardAlias("particles_simulated_selected", outputParticles)
0529 
0530     addSimWriters(
0531         s=s,
0532         simHits=alg.config.outputSimHits,
0533         particlesSimulated=outputParticles,
0534         field=field,
0535         outputDirCsv=outputDirCsv,
0536         outputDirRoot=outputDirRoot,
0537         outputDirObj=outputDirObj,
0538         logLevel=logLevel,
0539     )
0540 
0541     return s
0542 
0543 
0544 def addSimWriters(
0545     s: acts.examples.Sequencer,
0546     simHits: str = "simhits",
0547     particlesSimulated: str = "particles_simulated",
0548     field: acts.MagneticFieldProvider = None,
0549     writeHelixParameters: bool = False,
0550     outputDirCsv: Optional[Union[Path, str]] = None,
0551     outputDirRoot: Optional[Union[Path, str]] = None,
0552     outputDirObj: Optional[Union[Path, str]] = None,
0553     logLevel: Optional[acts.logging.Level] = None,
0554 ) -> None:
0555     customLogLevel = acts.examples.defaultLogging(s, logLevel)
0556 
0557     if outputDirCsv is not None:
0558         outputDirCsv = Path(outputDirCsv)
0559         if not outputDirCsv.exists():
0560             outputDirCsv.mkdir()
0561         s.addWriter(
0562             acts.examples.CsvParticleWriter(
0563                 level=customLogLevel(),
0564                 outputDir=str(outputDirCsv),
0565                 inputParticles=particlesSimulated,
0566                 outputStem="particles_simulated",
0567             )
0568         )
0569         s.addWriter(
0570             acts.examples.CsvSimHitWriter(
0571                 level=customLogLevel(),
0572                 inputSimHits=simHits,
0573                 outputDir=str(outputDirCsv),
0574                 outputStem="hits",
0575             )
0576         )
0577 
0578     if outputDirRoot is not None:
0579         outputDirRoot = Path(outputDirRoot)
0580         if not outputDirRoot.exists():
0581             outputDirRoot.mkdir()
0582         s.addWriter(
0583             RootParticleWriter(
0584                 level=customLogLevel(),
0585                 inputParticles=particlesSimulated,
0586                 bField=field,
0587                 writeHelixParameters=writeHelixParameters,
0588                 filePath=str(outputDirRoot / "particles_simulation.root"),
0589             )
0590         )
0591         s.addWriter(
0592             RootSimHitWriter(
0593                 level=customLogLevel(),
0594                 inputSimHits=simHits,
0595                 filePath=str(outputDirRoot / "hits.root"),
0596             )
0597         )
0598 
0599     if outputDirObj is not None:
0600         outputDirObj = Path(outputDirObj)
0601         if not outputDirObj.exists():
0602             outputDirObj.mkdir()
0603         s.addWriter(
0604             acts.examples.ObjSimHitWriter(
0605                 level=customLogLevel(),
0606                 inputSimHits=simHits,
0607                 outputDir=str(outputDirObj),
0608                 outputStem="hits",
0609             )
0610         )
0611 
0612 
0613 # holds the Geant4Handle for potential reuse
0614 __geant4Handle = None
0615 
0616 
0617 def addGeant4(
0618     s: acts.examples.Sequencer,
0619     detector: Optional[Any],
0620     trackingGeometry: acts.TrackingGeometry,
0621     field: acts.MagneticFieldProvider,
0622     rnd: acts.examples.RandomNumbers,
0623     volumeMappings: List[str] = [],
0624     materialMappings: List[str] = ["Silicon"],
0625     inputParticles: str = "particles_generated_selected",
0626     outputParticles: str = "particles_simulated",
0627     outputSimHits: str = "simhits",
0628     recordHitsOfSecondaries=True,
0629     keepParticlesWithoutHits=True,
0630     outputDirCsv: Optional[Union[Path, str]] = None,
0631     outputDirRoot: Optional[Union[Path, str]] = None,
0632     outputDirObj: Optional[Union[Path, str]] = None,
0633     logLevel: Optional[acts.logging.Level] = None,
0634     killVolume: Optional[acts.Volume] = None,
0635     killAfterTime: float = float("inf"),
0636     killSecondaries: bool = False,
0637     physicsList: str = "FTFP_BERT",
0638     regionList: List[Any] = [],
0639 ) -> None:
0640     """This function steers the detector simulation using Geant4
0641 
0642     Parameters
0643     ----------
0644     s: Sequencer
0645         the sequencer module to which we add the Geant4 steps (returned from addGeant4)
0646     trackingGeometry : tracking geometry or detector
0647     field : magnetic field
0648     rnd : RandomNumbers, None
0649         random number generator
0650     outputDirCsv : Path|str, path, None
0651         the output folder for the Csv output, None triggers no output
0652     outputDirRoot : Path|str, path, None
0653         the output folder for the Root output, None triggers no output
0654     outputDirObj : Path|str, path, None
0655         the output folder for the Obj output, None triggers no output
0656     killVolume: acts.Volume, None
0657         if given, particles are killed when going outside this volume.
0658     killAfterTime: float
0659         if given, particle are killed after the global time since event creation exceeds the given value
0660     killSecondaries: bool
0661         if given, secondary particles are removed from simulation
0662     """
0663 
0664     from acts.examples.geant4 import Geant4Simulation, SensitiveSurfaceMapper
0665 
0666     customLogLevel = acts.examples.defaultLogging(s, logLevel)
0667 
0668     global __geant4Handle
0669 
0670     smmConfig = SensitiveSurfaceMapper.Config()
0671     smmConfig.volumeMappings = volumeMappings
0672     smmConfig.materialMappings = materialMappings
0673     sensitiveMapper = SensitiveSurfaceMapper.create(
0674         smmConfig, customLogLevel(), trackingGeometry
0675     )
0676 
0677     alg = Geant4Simulation(
0678         level=customLogLevel(),
0679         geant4Handle=__geant4Handle,
0680         detector=detector,
0681         randomNumbers=rnd,
0682         inputParticles=inputParticles,
0683         outputParticles=outputParticles,
0684         outputSimHits=outputSimHits,
0685         sensitiveSurfaceMapper=sensitiveMapper,
0686         magneticField=field,
0687         physicsList=physicsList,
0688         killVolume=killVolume,
0689         killAfterTime=killAfterTime,
0690         killSecondaries=killSecondaries,
0691         recordHitsOfCharged=True,
0692         recordHitsOfNeutrals=False,
0693         recordHitsOfPrimaries=True,
0694         recordHitsOfSecondaries=recordHitsOfSecondaries,
0695         recordPropagationSummaries=False,
0696         keepParticlesWithoutHits=keepParticlesWithoutHits,
0697     )
0698     __geant4Handle = alg.geant4Handle
0699     s.addAlgorithm(alg)
0700 
0701     s.addWhiteboardAlias("particles", outputParticles)
0702 
0703     s.addWhiteboardAlias("particles_simulated_selected", outputParticles)
0704 
0705     addSimWriters(
0706         s=s,
0707         simHits=alg.config.outputSimHits,
0708         particlesSimulated=outputParticles,
0709         field=field,
0710         outputDirCsv=outputDirCsv,
0711         outputDirRoot=outputDirRoot,
0712         outputDirObj=outputDirObj,
0713         logLevel=logLevel,
0714     )
0715 
0716     return s
0717 
0718 
0719 def addSimParticleSelection(
0720     s: acts.examples.Sequencer,
0721     config: ParticleSelectorConfig,
0722     logLevel: Optional[acts.logging.Level] = None,
0723 ) -> None:
0724     """
0725     This function steers the particle selection after simulation.
0726 
0727     Parameters
0728     ----------
0729     s: Sequencer
0730         the sequencer module to which we add the ParticleSelector
0731     config: ParticleSelectorConfig
0732         the particle selection configuration
0733     """
0734     customLogLevel = acts.examples.defaultLogging(s, logLevel)
0735 
0736     selector = acts.examples.ParticleSelector(
0737         **acts.examples.defaultKWArgs(**_getParticleSelectionKWargs(config)),
0738         level=customLogLevel(),
0739         inputParticles="particles_simulated",
0740         outputParticles="tmp_particles_simulated_selected",
0741     )
0742     s.addAlgorithm(selector)
0743 
0744     s.addWhiteboardAlias("particles_selected", selector.config.outputParticles)
0745     s.addWhiteboardAlias(
0746         "particles_simulated_selected", selector.config.outputParticles
0747     )
0748 
0749 
0750 def addDigitization(
0751     s: acts.examples.Sequencer,
0752     trackingGeometry: acts.TrackingGeometry,
0753     field: acts.MagneticFieldProvider,
0754     digiConfigFile: Union[Path, str],
0755     outputDirCsv: Optional[Union[Path, str]] = None,
0756     outputDirRoot: Optional[Union[Path, str]] = None,
0757     rnd: Optional[acts.examples.RandomNumbers] = None,
0758     doMerge: Optional[bool] = None,
0759     mergeCommonCorner: Optional[bool] = None,
0760     minEnergyDeposit: Optional[float] = None,
0761     logLevel: Optional[acts.logging.Level] = None,
0762 ) -> acts.examples.Sequencer:
0763     """This function steers the digitization step
0764 
0765     Parameters
0766     ----------
0767     s: Sequencer
0768         the sequencer module to which we add the Digitization steps (returned from addDigitization)
0769     trackingGeometry : tracking geometry or detector
0770     field : magnetic field
0771     digiConfigFile : Path|str, path
0772         Configuration (.json) file for digitization or smearing description
0773     outputDirCsv : Path|str, path, None
0774         the output folder for the Csv output, None triggers no output
0775     outputDirRoot : Path|str, path, None
0776         the output folder for the Root output, None triggers no output
0777     rnd : RandomNumbers, None
0778         random number generator
0779     """
0780 
0781     customLogLevel = acts.examples.defaultLogging(s, logLevel)
0782 
0783     rnd = rnd or acts.examples.RandomNumbers()
0784 
0785     from acts.examples import json
0786 
0787     digiCfg = acts.examples.DigitizationAlgorithm.Config(
0788         digitizationConfigs=acts.examples.json.readDigiConfigFromJson(
0789             str(digiConfigFile),
0790         ),
0791         surfaceByIdentifier=trackingGeometry.geoIdSurfaceMap(),
0792         randomNumbers=rnd,
0793         inputSimHits="simhits",
0794         outputMeasurements="measurements",
0795         outputMeasurementParticlesMap="measurement_particles_map",
0796         outputMeasurementSimHitsMap="measurement_simhits_map",
0797         outputParticleMeasurementsMap="particle_measurements_map",
0798         outputSimHitMeasurementsMap="simhit_measurements_map",
0799         **acts.examples.defaultKWArgs(
0800             doMerge=doMerge,
0801             mergeCommonCorner=mergeCommonCorner,
0802         ),
0803     )
0804 
0805     # Not sure how to do this in our style
0806     if minEnergyDeposit is not None:
0807         digiCfg.minEnergyDeposit = minEnergyDeposit
0808 
0809     digiAlg = acts.examples.DigitizationAlgorithm(digiCfg, customLogLevel())
0810 
0811     s.addAlgorithm(digiAlg)
0812 
0813     if outputDirRoot is not None:
0814         outputDirRoot = Path(outputDirRoot)
0815         if not outputDirRoot.exists():
0816             outputDirRoot.mkdir()
0817         rmwConfig = acts.examples.root.RootMeasurementWriter.Config(
0818             inputMeasurements=digiAlg.config.outputMeasurements,
0819             inputClusters=digiAlg.config.outputClusters,
0820             inputSimHits=digiAlg.config.inputSimHits,
0821             inputMeasurementSimHitsMap=digiAlg.config.outputMeasurementSimHitsMap,
0822             filePath=str(outputDirRoot / f"{digiAlg.config.outputMeasurements}.root"),
0823             surfaceByIdentifier=trackingGeometry.geoIdSurfaceMap(),
0824         )
0825         s.addWriter(
0826             acts.examples.root.RootMeasurementWriter(rmwConfig, customLogLevel())
0827         )
0828 
0829     if outputDirCsv is not None:
0830         outputDirCsv = Path(outputDirCsv)
0831         if not outputDirCsv.exists():
0832             outputDirCsv.mkdir()
0833         s.addWriter(
0834             acts.examples.CsvMeasurementWriter(
0835                 level=customLogLevel(),
0836                 inputMeasurements=digiAlg.config.outputMeasurements,
0837                 inputClusters=digiAlg.config.outputClusters,
0838                 inputMeasurementSimHitsMap=digiAlg.config.outputMeasurementSimHitsMap,
0839                 outputDir=str(outputDirCsv),
0840             )
0841         )
0842 
0843     return s
0844 
0845 
0846 def addDigiParticleSelection(
0847     s: acts.examples.Sequencer,
0848     config: ParticleSelectorConfig,
0849     logLevel: Optional[acts.logging.Level] = None,
0850 ) -> None:
0851     """
0852     This function steers the particle selection after digitization.
0853 
0854     Parameters
0855     ----------
0856     s: Sequencer
0857         the sequencer module to which we add the ParticleSelector
0858     config: ParticleSelectorConfig
0859         the particle selection configuration
0860     """
0861     customLogLevel = acts.examples.defaultLogging(s, logLevel)
0862 
0863     selector = acts.examples.ParticleSelector(
0864         **acts.examples.defaultKWArgs(**_getParticleSelectionKWargs(config)),
0865         level=customLogLevel(),
0866         inputParticles="particles_simulated_selected",
0867         inputParticleMeasurementsMap="particle_measurements_map",
0868         inputMeasurements="measurements",
0869         outputParticles="tmp_particles_digitized_selected",
0870     )
0871     s.addAlgorithm(selector)
0872 
0873     s.addWhiteboardAlias("particles_selected", selector.config.outputParticles)
0874     s.addWhiteboardAlias(
0875         "particles_digitized_selected", selector.config.outputParticles
0876     )
0877 
0878 
0879 def addTruthJetAlg(
0880     s: acts.examples.Sequencer,
0881     config: TruthJetConfig,
0882     loglevel: Optional[acts.logging.Level] = None,
0883 ) -> None:
0884     from acts.examples.truthjet import TruthJetAlgorithm
0885 
0886     customLogLevel = acts.examples.defaultLogging(s, loglevel)
0887     truthJetAlg = acts.examples.truthjet.TruthJetAlgorithm(
0888         **acts.examples.defaultKWArgs(**_getTruthJetKWargs(config)),
0889         level=customLogLevel(),
0890     )
0891 
0892     s.addAlgorithm(truthJetAlg)