File indexing completed on 2025-01-18 09:14:22
0001 """
0002
0003 DD4hep simulation with some argument parsing
0004 Based on M. Frank and F. Gaede runSim.py
0005 @author A.Sailer
0006 @version 0.1
0007
0008 """
0009 import argparse
0010 import io
0011 import logging
0012 import os
0013 import sys
0014 import textwrap
0015 import traceback
0016 from urllib.parse import urlparse
0017 from DDSim.Helper.Meta import Meta
0018 from DDSim.Helper.LCIO import LCIO
0019 from DDSim.Helper.HepMC3 import HepMC3
0020 from DDSim.Helper.GuineaPig import GuineaPig
0021 from DDSim.Helper.Physics import Physics
0022 from DDSim.Helper.Filter import Filter
0023 from DDSim.Helper.Geometry import Geometry
0024 from DDSim.Helper.Random import Random
0025 from DDSim.Helper.Action import Action
0026 from DDSim.Helper.Output import Output, outputLevel, outputLevelType
0027 from DDSim.Helper.OutputConfig import OutputConfig, defaultOutputFile
0028 from DDSim.Helper.InputConfig import InputConfig
0029 from DDSim.Helper.ConfigHelper import ConfigHelper
0030 from DDSim.Helper.MagneticField import MagneticField
0031 from DDSim.Helper.ParticleHandler import ParticleHandler
0032 from DDSim.Helper.Gun import Gun
0033 from DDSim.Helper.UI import UI
0034
0035 logger = logging.getLogger('DDSim')
0036
0037 try:
0038 import argcomplete
0039 ARGCOMPLETEENABLED = True
0040 except ImportError:
0041 ARGCOMPLETEENABLED = False
0042
0043 HEPMC3_SUPPORTED_EXTENSIONS = [
0044 ".hepmc.gz", ".hepmc.xz", ".hepmc.bz2",
0045 ".hepmc3", ".hepmc3.gz", ".hepmc3.xz", ".hepmc3.bz2",
0046 ".hepmc3.tree.root",
0047 ]
0048 POSSIBLEINPUTFILES = [
0049 ".stdhep", ".slcio", ".HEPEvt", ".hepevt",
0050 ".pairs",
0051 ".hepmc",
0052 ] + HEPMC3_SUPPORTED_EXTENSIONS
0053
0054
0055 class DD4hepSimulation(object):
0056 """Class to hold all the parameters and functions to run simulation"""
0057
0058 def __init__(self):
0059 self.steeringFile = None
0060 self.compactFile = []
0061 self.inputFiles = []
0062 self.outputFile = defaultOutputFile()
0063 self.runType = "batch"
0064 self.printLevel = 3
0065
0066 self.numberOfEvents = 0
0067 self.skipNEvents = 0
0068 self.physicsList = None
0069 self.crossingAngleBoost = 0.0
0070 self.macroFile = ''
0071 self.enableGun = False
0072 self.enableG4GPS = False
0073 self.enableG4Gun = False
0074 self._g4gun = None
0075 self._g4gps = None
0076 self.vertexSigma = [0.0, 0.0, 0.0, 0.0]
0077 self.vertexOffset = [0.0, 0.0, 0.0, 0.0]
0078 self.enableDetailedShowerMode = False
0079 self.disableSignalHandler = False
0080
0081 self._errorMessages = []
0082 self._dumpParameter = False
0083 self._dumpSteeringFile = False
0084
0085
0086 self.output = Output()
0087 self.random = Random()
0088 self.gun = Gun()
0089 self.part = ParticleHandler()
0090 self.field = MagneticField()
0091 self.action = Action()
0092 self.outputConfig = OutputConfig()
0093 self.inputConfig = InputConfig()
0094 self.guineapig = GuineaPig()
0095 self.lcio = LCIO()
0096 self.hepmc3 = HepMC3()
0097 self.meta = Meta()
0098
0099 self.geometry = Geometry()
0100 self.filter = Filter()
0101 self.physics = Physics()
0102 self.ui = UI()
0103
0104 self._argv = None
0105
0106 def readSteeringFile(self):
0107 """Reads a steering file and sets the parameters to that of the
0108 DD4hepSimulation object present in the steering file.
0109 """
0110 globs = {}
0111 locs = {}
0112 if not self.steeringFile:
0113 return
0114 sFileTemp = self.steeringFile
0115 exec(compile(io.open(self.steeringFile).read(), self.steeringFile, 'exec'), globs, locs)
0116 for _name, obj in locs.items():
0117 if isinstance(obj, DD4hepSimulation):
0118 self.__dict__ = obj.__dict__
0119 self.steeringFile = os.path.abspath(sFileTemp)
0120
0121 def parseOptions(self, argv=None):
0122 """parse the command line options"""
0123
0124 if argv is None:
0125 self._argv = list(sys.argv)
0126
0127 parser = argparse.ArgumentParser("Running DD4hep Simulations:",
0128 formatter_class=argparse.RawTextHelpFormatter)
0129
0130 parser.add_argument("--steeringFile", "-S", action="store", default=self.steeringFile,
0131 help="Steering file to change default behaviour")
0132
0133
0134 if not any(opt in self._argv for opt in ('-h', '--help')):
0135 parsed, _unknown = parser.parse_known_args()
0136 self.steeringFile = parsed.steeringFile
0137 self.readSteeringFile()
0138
0139
0140 if self._argv is None:
0141 self._argv = list(argv) if argv else list(sys.argv)
0142
0143 parser.add_argument("--compactFile", nargs='+', action="store",
0144 default=ConfigHelper.makeList(self.compactFile), type=str,
0145 help="The compact XML file, or multiple compact files, if the last one is the closer.")
0146
0147 parser.add_argument("--runType", action="store", choices=("batch", "vis", "run", "shell", "qt"),
0148 default=self.runType,
0149 help="The type of action to do in this invocation"
0150 "\nbatch: just simulate some events, needs numberOfEvents, and input file or gun"
0151 "\nvis: enable visualisation, run the macroFile if it is set"
0152 "\nqt: enable visualisation in Qt shell, run the macroFile if it is set"
0153 "\nrun: run the macroFile and exit"
0154 "\nshell: enable interactive session")
0155
0156 parser.add_argument("--inputFiles", "-I", nargs='+', action="store", default=self.inputFiles,
0157 help="InputFiles for simulation %s files are supported" % ", ".join(POSSIBLEINPUTFILES))
0158
0159 parser.add_argument("--outputFile", "-O", action="store", default=self.outputFile,
0160 help="Outputfile from the simulation: .slcio, edm4hep.root and .root"
0161 " output files are supported")
0162
0163 parser.add_argument("-v", "--printLevel", action="store", default=self.printLevel, dest="printLevel",
0164 choices=(1, 2, 3, 4, 5, 6, 7, 'VERBOSE', 'DEBUG',
0165 'INFO', 'WARNING', 'ERROR', 'FATAL', 'ALWAYS'),
0166 type=outputLevelType,
0167 help="Verbosity use integers from 1(most) to 7(least) verbose"
0168 "\nor strings: VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL, ALWAYS")
0169
0170 parser.add_argument("--numberOfEvents", "-N", action="store", dest="numberOfEvents", default=self.numberOfEvents,
0171 type=int, help="number of events to simulate, used in batch mode")
0172
0173 parser.add_argument("--skipNEvents", action="store", dest="skipNEvents", default=self.skipNEvents, type=int,
0174 help="Skip first N events when reading a file")
0175
0176 parser.add_argument("--physicsList", action="store", dest="physicsList", default=self.physicsList,
0177 help="Physics list to use in simulation")
0178
0179 parser.add_argument("--crossingAngleBoost", action="store", dest="crossingAngleBoost",
0180 default=self.crossingAngleBoost,
0181 type=float, help="Lorentz boost for the crossing angle, in radian!")
0182
0183 parser.add_argument("--vertexSigma", nargs=4, action="store", dest="vertexSigma",
0184 default=self.vertexSigma, metavar=('X', 'Y', 'Z', 'T'),
0185 type=float, help="FourVector of the Sigma for the Smearing of the Vertex position: x y z t")
0186
0187 parser.add_argument("--vertexOffset", nargs=4, action="store", dest="vertexOffset",
0188 default=self.vertexOffset, metavar=('X', 'Y', 'Z', 'T'),
0189 type=float, help="FourVector of translation for the Smearing of the Vertex position: x y z t")
0190
0191 parser.add_argument("--macroFile", "-M", action="store", dest="macroFile", default=self.macroFile,
0192 help="Macro file to execute for runType 'run' or 'vis'")
0193
0194 parser.add_argument("--enableGun", "-G", action="store_true", dest="enableGun", default=self.enableGun,
0195 help="enable the DDG4 particle gun")
0196
0197 parser.add_argument("--enableG4GPS", action="store_true", dest="enableG4GPS", default=self.enableG4GPS,
0198 help="enable the Geant4 GeneralParticleSource. Needs a macroFile (runType run)"
0199 "or use it with the shell (runType shell)")
0200
0201 parser.add_argument("--enableG4Gun", action="store_true", dest="enableG4Gun", default=self.enableG4Gun,
0202 help="enable the Geant4 particle gun. Needs a macroFile (runType run)"
0203 " or use it with the shell (runType shell)")
0204
0205 parser.add_argument("--dumpParameter", "--dump", action="store_true", dest="dumpParameter",
0206 default=self._dumpParameter, help="Print all configuration Parameters and exit")
0207
0208 parser.add_argument("--enableDetailedShowerMode", action="store_true", dest="enableDetailedShowerMode",
0209 default=self.enableDetailedShowerMode,
0210 help="use detailed shower mode")
0211
0212 parser.add_argument("--disableSignalHandler", action="store_true", dest="disableSignalHandler",
0213 default=self.disableSignalHandler,
0214 help="disable the Signal Handler of DD4hep")
0215
0216 parser.add_argument("--dumpSteeringFile", action="store_true", dest="dumpSteeringFile",
0217 default=self._dumpSteeringFile, help="print an example steering file to stdout")
0218
0219
0220 ConfigHelper.addAllHelper(self, parser)
0221
0222
0223
0224 if ARGCOMPLETEENABLED:
0225 argcomplete.autocomplete(parser)
0226 parsed = parser.parse_args()
0227
0228 self._dumpParameter = parsed.dumpParameter
0229 self._dumpSteeringFile = parsed.dumpSteeringFile
0230
0231 self.compactFile = ConfigHelper.makeList(parsed.compactFile)
0232 self.__checkFilesExist(self.compactFile, fileType='compact')
0233 self.inputFiles = parsed.inputFiles
0234 self.inputFiles = self.__checkFileFormat(self.inputFiles, POSSIBLEINPUTFILES)
0235 self.__checkFilesExist(self.inputFiles, fileType='input')
0236 self.outputFile = parsed.outputFile
0237 self.__checkFileFormat(self.outputFile, ('.root', '.slcio'))
0238 self.runType = parsed.runType
0239 self.printLevel = self.__checkOutputLevel(parsed.printLevel)
0240
0241 self.numberOfEvents = parsed.numberOfEvents
0242 self.skipNEvents = parsed.skipNEvents
0243 self.physicsList = parsed.physicsList
0244 self.crossingAngleBoost = parsed.crossingAngleBoost
0245 self.macroFile = parsed.macroFile
0246 self.enableGun = parsed.enableGun
0247 self.enableG4Gun = parsed.enableG4Gun
0248 self.enableG4GPS = parsed.enableG4GPS
0249 self.enableDetailedShowerMode = parsed.enableDetailedShowerMode
0250 self.vertexOffset = parsed.vertexOffset
0251 self.vertexSigma = parsed.vertexSigma
0252
0253 self._consistencyChecks()
0254
0255 if self.printLevel <= 2:
0256 logger.setLevel(logging.DEBUG)
0257
0258
0259 self.__parseAllHelper(parsed)
0260 if self._errorMessages and not (self._dumpParameter or self._dumpSteeringFile):
0261 parser.epilog = "\n".join(self._errorMessages)
0262 parser.print_help()
0263 exit(1)
0264
0265 if self._dumpParameter:
0266 from pprint import pprint
0267 logger.info("=" * 80)
0268 pprint(vars(self))
0269 logger.info("=" * 80)
0270 exit(0)
0271
0272 if self._dumpSteeringFile:
0273 self.__printSteeringFile(parser)
0274 exit(0)
0275
0276 def getDetectorLists(self, detectorDescription):
0277 ''' get lists of trackers and calorimeters that are defined in detectorDescription (the compact xml file)'''
0278 import DDG4
0279 trackers, calos, unknown = [], [], []
0280 for i in detectorDescription.detectors():
0281 det = DDG4.DetElement(i.second.ptr())
0282 name = det.name()
0283 sd = detectorDescription.sensitiveDetector(name)
0284 if sd.isValid():
0285 detType = sd.type()
0286 logger.info('getDetectorLists - found active detector %s type: %s', name, detType)
0287 if any(pat.lower() in detType.lower() for pat in self.action.trackerSDTypes):
0288 logger.info('getDetectorLists - Identified %s as a tracker', name)
0289 trackers.append(det.name())
0290 elif any(pat.lower() in detType.lower() for pat in self.action.calorimeterSDTypes):
0291 logger.info('getDetectorLists - Identified %s as a calorimeter', name)
0292 calos.append(det.name())
0293 else:
0294 logger.warning('getDetectorLists - Unknown sensitive detector type: %s', detType)
0295 unknown.append(det.name())
0296
0297 return trackers, calos, unknown
0298
0299
0300
0301 def run(self):
0302 """setup the geometry and dd4hep and geant4 and do what was asked to be done"""
0303 import ROOT
0304 ROOT.PyConfig.IgnoreCommandLineOptions = True
0305
0306 import DDG4
0307 import dd4hep
0308
0309 self.printLevel = getOutputLevel(self.printLevel)
0310
0311 kernel = DDG4.Kernel()
0312 dd4hep.setPrintLevel(self.printLevel)
0313
0314 for compactFile in self.compactFile:
0315 kernel.loadGeometry(str("file:" + os.path.abspath(compactFile)))
0316 detectorDescription = kernel.detectorDescription()
0317
0318 DDG4.importConstants(detectorDescription)
0319
0320
0321
0322
0323
0324 geant4 = DDG4.Geant4(kernel, tracker=self.action.tracker, calo=self.action.calo)
0325 if not self.disableSignalHandler:
0326 geant4.registerInterruptHandler()
0327
0328 geant4.printDetectors()
0329
0330 if self.runType == "vis":
0331 uiaction = geant4.setupUI(typ="tcsh", vis=True, macro=self.macroFile)
0332 elif self.runType == "qt":
0333 uiaction = geant4.setupUI(typ="qt", vis=True, macro=self.macroFile)
0334 elif self.runType == "run":
0335 uiaction = geant4.setupUI(typ="tcsh", vis=False, macro=self.macroFile, ui=False)
0336 elif self.runType == "shell":
0337 uiaction = geant4.setupUI(typ="tcsh", vis=False, macro=None, ui=True)
0338 elif self.runType == "batch":
0339 uiaction = geant4.setupUI(typ="tcsh", vis=False, macro=None, ui=False)
0340 else:
0341 logger.error("unknown runType")
0342 return 1
0343
0344
0345 uiaction.ConfigureCommands = self.ui._commandsConfigure
0346 uiaction.InitializeCommands = self.ui._commandsInitialize
0347 uiaction.PostRunCommands = self.ui._commandsPostRun
0348 uiaction.PreRunCommands = self.ui._commandsPreRun
0349 uiaction.TerminateCommands = self.ui._commandsTerminate
0350
0351 kernel.NumEvents = self.numberOfEvents
0352
0353
0354
0355 self.__setMagneticFieldOptions(geant4)
0356
0357
0358 self.geometry.constructGeometry(kernel, geant4, self.output.geometry)
0359
0360
0361
0362 for action_list, DDG4_Action, kernel_Action in \
0363 [(self.action.run, DDG4.RunAction, kernel.runAction),
0364 (self.action.event, DDG4.EventAction, kernel.eventAction),
0365 (self.action.track, DDG4.TrackingAction, kernel.trackingAction),
0366 (self.action.step, DDG4.SteppingAction, kernel.steppingAction),
0367 (self.action.stack, DDG4.StackingAction, kernel.stackingAction)]:
0368 for action_dict in action_list:
0369 action = DDG4_Action(kernel, action_dict["name"])
0370 for parameter, value in action_dict.get('parameter', {}).items():
0371 setattr(action, parameter, value)
0372 kernel_Action().add(action)
0373
0374
0375
0376 run1 = DDG4.RunAction(kernel, 'Geant4TestRunAction/RunInit')
0377 kernel.registerGlobalAction(run1)
0378 kernel.runAction().add(run1)
0379
0380
0381 self.random.initialize(DDG4, kernel, self.output.random)
0382
0383
0384 self.outputConfig.initialize(dd4hepsimulation=self, geant4=geant4)
0385
0386 actionList = []
0387
0388 if self.enableGun:
0389 gun = DDG4.GeneratorAction(kernel, "Geant4ParticleGun/" + "Gun")
0390 self.gun.setOptions(gun)
0391 gun.Standalone = False
0392 gun.Mask = 1
0393 actionList.append(gun)
0394 self.__applyBoostOrSmear(kernel, actionList, 1)
0395 logger.info("++++ Adding DD4hep Particle Gun ++++")
0396
0397 if self.enableG4Gun:
0398
0399 self._g4gun = DDG4.GeneratorAction(kernel, "Geant4GeneratorWrapper/Gun")
0400 self._g4gun.Uses = 'G4ParticleGun'
0401 self._g4gun.Mask = 2
0402 logger.info("++++ Adding Geant4 Particle Gun ++++")
0403 actionList.append(self._g4gun)
0404
0405 if self.enableG4GPS:
0406
0407 self._g4gps = DDG4.GeneratorAction(kernel, "Geant4GeneratorWrapper/GPS")
0408 self._g4gps.Uses = 'G4GeneralParticleSource'
0409 self._g4gps.Mask = 3
0410 logger.info("++++ Adding Geant4 General Particle Source ++++")
0411 actionList.append(self._g4gps)
0412
0413 start = 4
0414 for index, plugin in enumerate(self.inputConfig.userInputPlugin, start=start):
0415 gen = plugin(self)
0416 gen.Mask = index
0417 start = index + 1
0418 actionList.append(gen)
0419 self.__applyBoostOrSmear(kernel, actionList, index)
0420 logger.info("++++ Adding User Plugin %s ++++", gen.Name)
0421
0422 for index, inputFile in enumerate(self.inputFiles, start=start):
0423 if inputFile.endswith(".slcio"):
0424 gen = DDG4.GeneratorAction(kernel, "LCIOInputAction/LCIO%d" % index)
0425 gen.Parameters = self.lcio.getParameters()
0426 gen.Input = "LCIOFileReader|" + inputFile
0427 elif inputFile.endswith(".stdhep"):
0428 gen = DDG4.GeneratorAction(kernel, "LCIOInputAction/STDHEP%d" % index)
0429 gen.Input = "LCIOStdHepReader|" + inputFile
0430 elif inputFile.endswith(".HEPEvt"):
0431 gen = DDG4.GeneratorAction(kernel, "Geant4InputAction/HEPEvt%d" % index)
0432 gen.Input = "Geant4EventReaderHepEvtShort|" + inputFile
0433 elif inputFile.endswith(".hepevt"):
0434 gen = DDG4.GeneratorAction(kernel, "Geant4InputAction/hepevt%d" % index)
0435 gen.Input = "Geant4EventReaderHepEvtLong|" + inputFile
0436 elif inputFile.endswith(tuple([".hepmc"] + HEPMC3_SUPPORTED_EXTENSIONS)):
0437 if self.hepmc3.useHepMC3:
0438 gen = DDG4.GeneratorAction(kernel, "Geant4InputAction/hepmc%d" % index)
0439 gen.Parameters = self.hepmc3.getParameters()
0440 gen.Input = "HEPMC3FileReader|" + inputFile
0441 else:
0442 gen = DDG4.GeneratorAction(kernel, "Geant4InputAction/hepmc%d" % index)
0443 gen.Input = "Geant4EventReaderHepMC|" + inputFile
0444 elif inputFile.endswith(".pairs"):
0445 gen = DDG4.GeneratorAction(kernel, "Geant4InputAction/GuineaPig%d" % index)
0446 gen.Input = "Geant4EventReaderGuineaPig|" + inputFile
0447 gen.Parameters = self.guineapig.getParameters()
0448 else:
0449
0450 raise RuntimeError("Unknown input file type: %s" % inputFile)
0451 gen.AlternativeDecayStatuses = self.physics.alternativeDecayStatuses
0452 gen.Sync = self.skipNEvents
0453 gen.Mask = index
0454 actionList.append(gen)
0455 self.__applyBoostOrSmear(kernel, actionList, index)
0456
0457 generationInit = None
0458 if actionList:
0459 generationInit = self._buildInputStage(geant4, actionList, output_level=self.output.inputStage,
0460 have_mctruth=self._enablePrimaryHandler())
0461
0462
0463
0464
0465 part = DDG4.GeneratorAction(kernel, "Geant4ParticleHandler/ParticleHandler")
0466 kernel.generatorAction().adopt(part)
0467
0468 part.SaveProcesses = self.part.saveProcesses
0469 part.MinimalKineticEnergy = self.part.minimalKineticEnergy
0470 part.KeepAllParticles = self.part.keepAllParticles
0471 part.PrintEndTracking = self.part.printEndTracking
0472 part.PrintStartTracking = self.part.printStartTracking
0473 part.MinDistToParentVertex = self.part.minDistToParentVertex
0474 part.OutputLevel = self.output.part
0475 part.enableUI()
0476
0477 if self.part.enableDetailedHitsAndParticleInfo:
0478 self.part.setDumpDetailedParticleInfo(kernel, DDG4)
0479
0480 self.part.setupUserParticleHandler(part, kernel, DDG4)
0481
0482
0483
0484
0485 try:
0486 self.filter.setupFilters(kernel)
0487 except RuntimeError as e:
0488 logger.error("%s", e)
0489 return 1
0490
0491
0492
0493
0494 trk, cal, unk = self.getDetectorLists(detectorDescription)
0495 for detectors, function, defFilter, defAction, abort in \
0496 [(trk, geant4.setupTracker, self.filter.tracker, self.action.tracker, False),
0497 (cal, geant4.setupCalorimeter, self.filter.calo, self.action.calo, False),
0498 (unk, geant4.setupDetector, None, "No Default", True),
0499 ]:
0500 try:
0501 self.__setupSensitiveDetectors(detectors, function, defFilter, defAction, abort)
0502 except Exception as e:
0503 logger.error("Failed setting up sensitive detector %s", e)
0504 raise
0505
0506
0507
0508 _phys = self.physics.setupPhysics(kernel, name=self.physicsList)
0509 _phys.verbosity = self.output.physics
0510
0511
0512 ph = DDG4.PhysicsList(kernel, 'Geant4PhysicsList/Myphysics')
0513 ph.addPhysicsConstructor(str('G4StepLimiterPhysics'))
0514 _phys.add(ph)
0515
0516 dd4hep.setPrintLevel(self.printLevel)
0517
0518 kernel.configure()
0519 kernel.initialize()
0520
0521
0522 if self._g4gun is not None:
0523 self._g4gun.generator()
0524 if self._g4gps is not None:
0525 self._g4gps.generator()
0526
0527 startUpTime, _sysTime, _cuTime, _csTime, _elapsedTime = os.times()
0528
0529 exitCode = 0
0530 if not kernel.run():
0531 logger.error("Simulation failed!")
0532 exitCode += 1
0533 if not kernel.terminate():
0534 exitCode += 1
0535 logger.error("Termination failed!")
0536
0537 totalTimeUser, totalTimeSys, _cuTime, _csTime, _elapsedTime = os.times()
0538 processedEvents = self.numberOfEvents
0539 if generationInit:
0540 processedEvents = int(generationInit.numberOfEvents)
0541 if self.numberOfEvents < 0:
0542 processedEvents -= 1
0543 logger.debug(f"Correcting number of events to: {processedEvents}")
0544
0545 if self.printLevel <= 3:
0546 logger.info("Total Time: %3.2f s (User), %3.2f s (System)" %
0547 (totalTimeUser, totalTimeSys))
0548 if processedEvents != 0:
0549 eventTime = totalTimeUser - startUpTime
0550 perEventTime = eventTime / processedEvents
0551 logger.info("StartUp Time: %3.2f s, Event Processing: %3.2f s (%3.2f s/Event) "
0552 % (startUpTime, eventTime, perEventTime))
0553 return exitCode
0554
0555 def __setMagneticFieldOptions(self, geant4):
0556 """ create and configure the magnetic tracking setup """
0557 field = geant4.addConfig('Geant4FieldTrackingSetupAction/MagFieldTrackingSetup')
0558 field.stepper = self.field.stepper
0559 field.equation = self.field.equation
0560 field.eps_min = self.field.eps_min
0561 field.eps_max = self.field.eps_max
0562 field.min_chord_step = self.field.min_chord_step
0563 field.delta_chord = self.field.delta_chord
0564 field.delta_intersection = self.field.delta_intersection
0565 field.delta_one_step = self.field.delta_one_step
0566 field.largest_step = self.field.largest_step
0567
0568 def __checkFilesExist(self, fileNames, fileType=''):
0569 """Make sure all files in the given list exist, add to errorMessage otherwise.
0570
0571
0572 :param list fileNames: list of files to check for existence
0573 :param str fileType: type if file, for nicer error message
0574 """
0575 if isinstance(fileNames, str):
0576 fileNames = [fileNames]
0577 for fileName in fileNames:
0578 if not os.path.exists(fileName) and not urlparse(fileName).scheme:
0579 self._errorMessages.append(f"ERROR: The {fileType}file '{fileName}' does not exist")
0580
0581 def __checkFileFormat(self, fileNames, extensions):
0582 """check if the fileName is allowed, note that the filenames are case
0583 sensitive, and in case of hepevt we depend on this to identify short and long versions of the content
0584 """
0585 if isinstance(fileNames, str):
0586 fileNames = [fileNames]
0587 if not all(fileName.endswith(tuple(extensions)) for fileName in fileNames):
0588 self._errorMessages.append(f"ERROR: Unknown fileformat for file(s): {','.join(fileNames)}")
0589 is_hepmc3_extension = any(fileName.endswith(tuple(HEPMC3_SUPPORTED_EXTENSIONS)) for fileName in fileNames)
0590 if not self.hepmc3.useHepMC3 and is_hepmc3_extension:
0591 self._errorMessages.append("ERROR: HepMC3 files or compressed HepMC2 require the use of HepMC3 library")
0592 return fileNames
0593
0594 def __applyBoostOrSmear(self, kernel, actionList, mask):
0595 """apply boost or smearing for given mask index"""
0596 import DDG4
0597 if self.crossingAngleBoost:
0598 lbo = DDG4.GeneratorAction(kernel, "Geant4InteractionVertexBoost")
0599 lbo.Angle = self.crossingAngleBoost
0600 lbo.Mask = mask
0601 actionList.append(lbo)
0602
0603 if any(self.vertexSigma) or any(self.vertexOffset):
0604 vSmear = DDG4.GeneratorAction(kernel, "Geant4InteractionVertexSmear")
0605 vSmear.Offset = self.vertexOffset
0606 vSmear.Sigma = self.vertexSigma
0607 vSmear.Mask = mask
0608 actionList.append(vSmear)
0609
0610 def __parseAllHelper(self, parsed):
0611 """ parse all the options for the helper """
0612 parsedDict = vars(parsed)
0613 for name, obj in vars(self).items():
0614 if isinstance(obj, ConfigHelper):
0615 for var in obj.getOptions():
0616 key = "%s.%s" % (name, var)
0617 if key in parsedDict:
0618 try:
0619 obj.setOption(var, parsedDict[key])
0620 except RuntimeError as e:
0621 self._errorMessages.append("ERROR: %s " % e)
0622 if logger.level <= logging.DEBUG:
0623 self._errorMessages.append(traceback.format_exc())
0624 obj._checkProperties()
0625
0626 def __checkOutputLevel(self, level):
0627 """return outputlevel as int so we don't have to import anything for faster startup"""
0628 try:
0629 return outputLevel(level)
0630 except ValueError:
0631 self._errorMessages.append("ERROR: printLevel is neither integer nor string")
0632 return -1
0633 except KeyError:
0634 self._errorMessages.append("ERROR: printLevel '%s' unknown" % level)
0635 return -1
0636
0637 def __setupSensitiveDetectors(self, detectors, setupFunction, defaultFilter=None,
0638 defaultAction=None, abortForMissingAction=False,
0639 ):
0640 """Attach sensitive detector actions for all subdetectors.
0641
0642 Can be steered with the `Action` ConfigHelpers
0643
0644 :param detectors: list of detectors
0645 :param setupFunction: function used to register the sensitive detector
0646 :param defaultFilter: default filter to apply for given types
0647 :param abortForMissingAction: if true end program if there is no action found
0648 """
0649 for det in detectors:
0650 logger.info('Setting up SD for %s with %s', det, defaultAction)
0651 action = None
0652 for pattern in self.action.mapActions:
0653 if pattern.lower() in det.lower():
0654 action = self.action.mapActions[pattern]
0655 logger.info(' replace default action with : %s', action)
0656 break
0657 if abortForMissingAction and action is None:
0658 logger.error('Cannot find Action for detector %s. You have to extend "action.mapAction"', det)
0659 raise RuntimeError("Cannot find Action")
0660 seq, act = setupFunction(det, action)
0661 self.filter.applyFilters(seq, det, defaultFilter)
0662
0663
0664 if self.enableDetailedShowerMode:
0665 if isinstance(act, list):
0666 for a in act:
0667 a.HitCreationMode = 2
0668 else:
0669 act.HitCreationMode = 2
0670
0671 def __printSteeringFile(self, parser):
0672 """print the parameters formated as a steering file"""
0673
0674 steeringFileBase = textwrap.dedent("""\
0675 from DDSim.DD4hepSimulation import DD4hepSimulation
0676 from g4units import mm, GeV, MeV
0677 SIM = DD4hepSimulation()
0678 """)
0679 steeringFileBase += "\n"
0680 optionDict = parser._option_string_actions
0681 parameters = vars(self)
0682 for parName, parameter in sorted(list(parameters.items()), key=sortParameters):
0683 if parName.startswith("_"):
0684 continue
0685 if isinstance(parameter, ConfigHelper):
0686 steeringFileBase += "\n\n"
0687 steeringFileBase += "################################################################################\n"
0688 steeringFileBase += "## %s \n" % "\n## ".join(parameter.__doc__.splitlines())
0689 steeringFileBase += "################################################################################\n"
0690 options = parameter.getOptions()
0691 for opt, optionsDict in sorted(options.items(), key=sortParameters):
0692 if opt.startswith("_"):
0693 continue
0694 parValue = optionsDict['default']
0695 if isinstance(optionsDict.get('help'), str):
0696 steeringFileBase += "\n## %s\n" % "\n## ".join(optionsDict.get('help').splitlines())
0697
0698 if isinstance(parValue, str):
0699 steeringFileBase += "SIM.%s.%s = \"%s\"\n" % (parName, opt, parValue)
0700 else:
0701 steeringFileBase += "SIM.%s.%s = %s\n" % (parName, opt, parValue)
0702 else:
0703
0704 optionObj = optionDict.get("--" + parName, None)
0705 if isinstance(optionObj, argparse._StoreAction):
0706 steeringFileBase += "## %s\n" % "\n## ".join(optionObj.help.splitlines())
0707
0708 if isinstance(parameter, str):
0709 steeringFileBase += "SIM.%s = \"%s\"" % (parName, str(parameter))
0710 else:
0711 steeringFileBase += "SIM.%s = %s" % (parName, str(parameter))
0712 steeringFileBase += "\n"
0713 for line in steeringFileBase.splitlines():
0714 print(line)
0715
0716 def _consistencyChecks(self):
0717 """Check if the requested setup makes sense, or if there is something preventing it from working correctly
0718
0719 Appends error messages to self._errorMessages
0720
0721 :returns: None
0722 """
0723
0724 if not self.compactFile:
0725 self._errorMessages.append("ERROR: No geometry compact file provided")
0726
0727 if self.runType == "batch":
0728 if not self.numberOfEvents:
0729 self._errorMessages.append("ERROR: Batch mode requested, but did not set number of events")
0730 if not (self.inputFiles or self.enableGun or self.inputConfig.userInputPlugin):
0731 self._errorMessages.append("ERROR: Batch mode requested, but did not set inputFile(s), gun, or userInputPlugin")
0732
0733 if self.inputFiles and (self.enableG4Gun or self.enableG4GPS):
0734 self._errorMessages.append("ERROR: Cannot use both inputFiles and Geant4Gun or GeneralParticleSource")
0735
0736 if self.enableGun and (self.enableG4Gun or self.enableG4GPS):
0737 self._errorMessages.append("ERROR: Cannot use both DD4hepGun and Geant4 Gun or GeneralParticleSource")
0738
0739 if self.inputConfig.userInputPlugin and (self.enableG4Gun or self.enableG4GPS):
0740 self._errorMessages.append("ERROR: Cannot use both userInputPlugin and Geant4 Gun or GeneralParticleSource")
0741
0742 if self.numberOfEvents < 0 and not self.inputFiles:
0743 self._errorMessages.append("ERROR: Negative number of events only sensible for inputFiles")
0744
0745 def _enablePrimaryHandler(self):
0746 """ the geant4 Gun or GeneralParticleSource cannot be used together with the PrimaryHandler.
0747 Particles would be simulated multiple times
0748
0749 :returns: True or False
0750 """
0751 enablePrimaryHandler = not (self.enableG4Gun or self.enableG4GPS)
0752 if enablePrimaryHandler:
0753 logger.info("Enabling the PrimaryHandler")
0754 else:
0755 logger.info("Disabling the PrimaryHandler")
0756 return enablePrimaryHandler
0757
0758 def _buildInputStage(self, geant4, generator_input_modules, output_level=None, have_mctruth=True):
0759 """
0760 Generic build of the input stage with multiple input modules.
0761 Actions executed are:
0762 1) Register Generation initialization action
0763 2) Append all modules to build the complete input record
0764 These modules are readers/particle sources, boosters and/or smearing actions.
0765 3) Merge all existing interaction records
0766 4) Add the MC truth handler
0767 """
0768 from DDG4 import GeneratorAction
0769 ga = geant4.kernel().generatorAction()
0770
0771
0772 gen = GeneratorAction(geant4.kernel(), "Geant4GeneratorActionInit/GenerationInit")
0773 generationInit = gen
0774 if output_level is not None:
0775 gen.OutputLevel = output_level
0776 ga.adopt(gen)
0777
0778
0779
0780 for gen in generator_input_modules:
0781 gen.enableUI()
0782 if output_level is not None:
0783 gen.OutputLevel = output_level
0784 ga.adopt(gen)
0785
0786
0787 gen = GeneratorAction(geant4.kernel(), "Geant4InteractionMerger/InteractionMerger")
0788 gen.enableUI()
0789 if output_level is not None:
0790 gen.OutputLevel = output_level
0791 ga.adopt(gen)
0792
0793
0794 if have_mctruth:
0795 gen = GeneratorAction(geant4.kernel(), "Geant4PrimaryHandler/PrimaryHandler")
0796 gen.RejectPDGs = ConfigHelper.makeString(self.physics.rejectPDGs)
0797 gen.ZeroTimePDGs = ConfigHelper.makeString(self.physics.zeroTimePDGs)
0798 gen.enableUI()
0799 if output_level is not None:
0800 gen.OutputLevel = output_level
0801 ga.adopt(gen)
0802
0803 return generationInit
0804
0805
0806
0807
0808
0809
0810
0811 def sortParameters(key):
0812 from functools import cmp_to_key
0813
0814 def _sortParameters(parA, parB):
0815 """sort the parameters by name: first normal parameters, then set of
0816 parameters based on ConfigHelper objects
0817 """
0818 parTypeA = parA[1]
0819 parTypeB = parB[1]
0820 if isinstance(parTypeA, ConfigHelper) and isinstance(parTypeB, ConfigHelper):
0821 return 1 if str(parA[0]) > str(parB[0]) else -1
0822 elif isinstance(parTypeA, ConfigHelper):
0823 return 1
0824 elif isinstance(parTypeB, ConfigHelper):
0825 return -1
0826 else:
0827 return 1 if str(parA[0]) > str(parB[0]) else -1
0828
0829 return cmp_to_key(_sortParameters)(key)
0830
0831
0832 def getOutputLevel(level):
0833 """return output.LEVEL"""
0834 from DDG4 import OutputLevel
0835 levels = {1: OutputLevel.VERBOSE,
0836 2: OutputLevel.DEBUG,
0837 3: OutputLevel.INFO,
0838 4: OutputLevel.WARNING,
0839 5: OutputLevel.ERROR,
0840 6: OutputLevel.FATAL,
0841 7: OutputLevel.ALWAYS}
0842 return levels[level]