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
0026
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",
0045 "absZ",
0046 "time",
0047 "phi",
0048 "eta",
0049 "absEta",
0050 "pt",
0051 "m",
0052 "hits",
0053 "measurements",
0054 "removeCharged",
0055 "removeNeutral",
0056 "removeSecondaries",
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
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,
0261 cmsEnergy: Optional[float] = None,
0262 hardProcess: Optional[Iterable] = None,
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
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
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)