File indexing completed on 2026-06-14 07:49:43
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 import logging
0012 import signal
0013 import cppyy
0014 from dd4hep_base import *
0015
0016 logger = logging.getLogger(__name__)
0017
0018
0019
0020 def loadDDG4():
0021 import ROOT
0022 from ROOT import gSystem
0023
0024
0025
0026 if 'libglapi' not in gSystem.GetLibraries():
0027 orgLevel = ROOT.gErrorIgnoreLevel
0028 ROOT.gErrorIgnoreLevel = 6000
0029 gSystem.Load("libglapi")
0030 ROOT.gErrorIgnoreLevel = orgLevel
0031
0032 import platform
0033 import os
0034 if platform.system() == "Darwin":
0035 gSystem.SetDynamicPath(os.environ['DD4HEP_LIBRARY_PATH'])
0036 os.environ['DYLD_LIBRARY_PATH'] = os.pathsep.join([os.environ['DD4HEP_LIBRARY_PATH'],
0037 os.environ.get('DYLD_LIBRARY_PATH', '')]).strip(os.pathsep)
0038
0039 result = gSystem.Load("libDDG4Plugins")
0040 if result < 0:
0041 raise Exception('DDG4.py: Failed to load the DDG4 library libDDG4Plugins: ' + gSystem.GetErrorStr())
0042 from ROOT import dd4hep as module
0043 return module
0044
0045
0046
0047 current = __import__(__name__)
0048
0049
0050
0051 def _import_class(ns, nam):
0052 scope = getattr(current, ns)
0053 setattr(current, nam, getattr(scope, nam))
0054
0055
0056
0057 try:
0058 dd4hep = loadDDG4()
0059 except Exception as X:
0060 logger.error('+--%-100s--+', 100 * '-')
0061 logger.error('| %-100s |', 'Failed to load DDG4 library:')
0062 logger.error('| %-100s |', str(X))
0063 logger.error('+--%-100s--+', 100 * '-')
0064 exit(1)
0065
0066
0067 from ROOT import CLHEP as CLHEP
0068 Core = dd4hep
0069 Sim = dd4hep.sim
0070 Simulation = dd4hep.sim
0071 Kernel = Sim.KernelHandle
0072 Interface = Sim.Geant4ActionCreation
0073 Detector = Core.Detector
0074
0075
0076
0077 def _constant(self, name):
0078 return self.constantAsString(name)
0079
0080
0081 Detector.globalVal = _constant
0082
0083
0084
0085 def importConstants(description, namespace=None, debug=False):
0086 """
0087 Import the Detector constants into the DDG4 namespace
0088 """
0089 ns = current
0090 if namespace is not None and not hasattr(current, namespace):
0091 import types
0092 m = types.ModuleType('DDG4.' + namespace)
0093 setattr(current, namespace, m)
0094 ns = m
0095 evaluator = dd4hep.g4Evaluator()
0096 cnt = 0
0097 num = 0
0098 todo = {}
0099 strings = {}
0100 for c in description.constants():
0101 if c.second.dataType == 'string':
0102 strings[str(c.first)] = c.second.GetTitle()
0103 else:
0104 todo[str(c.first)] = c.second.GetTitle().replace('(int)', '')
0105 while len(todo) and cnt < 100:
0106 cnt = cnt + 1
0107 if cnt == 100:
0108 logger.error('%s %d out of %d %s "%s": [%s]\n+++ %s',
0109 '+++ FAILED to import',
0110 len(todo), len(todo) + num,
0111 'global values into namespace',
0112 ns.__name__, 'Try to continue anyway', 100 * '=')
0113 for k, v in todo.items():
0114 if not hasattr(ns, k):
0115 logger.error('+++ FAILED to import: "' + k + '" = "' + str(v) + '"')
0116 logger.info('+++ %s', 100 * '=')
0117
0118 for k, v in list(todo.items()):
0119 if not hasattr(ns, k):
0120 val = evaluator.evaluate(str(v))
0121 if val.first == 0:
0122 evaluator.setVariable(str(k), val.second)
0123 setattr(ns, k, val.second)
0124 if debug:
0125 logger.info('Imported global value: "' + k + '" = "' + str(val.second) + '" into namespace' + ns.__name__)
0126 del todo[k]
0127 num = num + 1
0128 if cnt < 100:
0129 logger.info('+++ Imported %d global values to namespace:%s', num, ns.__name__,)
0130
0131
0132
0133 def _registerGlobalAction(self, action):
0134 self.get().registerGlobalAction(Interface.toAction(action))
0135
0136
0137
0138 def _registerGlobalFilter(self, filter):
0139 self.get().registerGlobalFilter(Interface.toAction(filter))
0140
0141
0142
0143 def _evalProperty(data):
0144 """
0145 Function necessary to extract real strings from the property value.
0146 Strings may be embraced by quotes: '<value>', or could be cppyy.gbl.std.string with extra "b''"
0147 """
0148 try:
0149 if isinstance(data, (cppyy.gbl.std.string, )):
0150 return _evalProperty(data.decode('utf-8'))
0151 if isinstance(data, str):
0152 import ast
0153 return ast.literal_eval(data)
0154 except ValueError:
0155 pass
0156 except TypeError:
0157 pass
0158 finally:
0159 pass
0160 return data
0161
0162
0163
0164 def _getKernelProperty(self, name):
0165 ret = Interface.getPropertyKernel(self.get(), name)
0166 if ret.status > 0:
0167 return _evalProperty(ret.data)
0168 elif hasattr(self.get(), name):
0169 return _evalProperty(getattr(self.get(), name))
0170 msg = 'Geant4Kernel::GetProperty [Unhandled]: Cannot access Kernel.' + name
0171 raise KeyError(msg)
0172
0173
0174
0175 def _setKernelProperty(self, name, value):
0176 if Interface.setPropertyKernel(self.get(), str(name), str(value)):
0177 return
0178 msg = 'Geant4Kernel::SetProperty [Unhandled]: Cannot set Kernel.' + name + ' = ' + str(value)
0179 raise KeyError(msg)
0180
0181
0182
0183 def _kernel_phase(self, name):
0184 return self.addSimplePhase(str(name), False)
0185
0186
0187
0188 def _kernel_worker(self):
0189 return Kernel(self.get().createWorker())
0190
0191
0192
0193 def _kernel_terminate(self):
0194 return self.get().terminate()
0195
0196
0197 Kernel.phase = _kernel_phase
0198 Kernel.registerGlobalAction = _registerGlobalAction
0199 Kernel.registerGlobalFilter = _registerGlobalFilter
0200 Kernel.createWorker = _kernel_worker
0201 Kernel.__getattr__ = _getKernelProperty
0202 Kernel.__setattr__ = _setKernelProperty
0203 Kernel.terminate = _kernel_terminate
0204
0205 ActionHandle = Sim.ActionHandle
0206
0207
0208
0209 def SensitiveAction(kernel, nam, det, shared=False):
0210 return Interface.createSensitive(kernel, str(nam), str(det), shared)
0211
0212
0213
0214 def Action(kernel, nam, shared=False):
0215 return Interface.createAction(kernel, str(nam), shared)
0216
0217
0218
0219 def Filter(kernel, nam, shared=False):
0220 return Interface.createFilter(kernel, str(nam), shared)
0221
0222
0223
0224 def PhaseAction(kernel, nam, shared=False):
0225 return Interface.createPhaseAction(kernel, str(nam), shared)
0226
0227
0228
0229 def RunAction(kernel, nam, shared=False):
0230 return Interface.createRunAction(kernel, str(nam), shared)
0231
0232
0233
0234 def EventAction(kernel, nam, shared=False):
0235 return Interface.createEventAction(kernel, str(nam), shared)
0236
0237
0238
0239 def GeneratorAction(kernel, nam, shared=False):
0240 return Interface.createGeneratorAction(kernel, str(nam), shared)
0241
0242
0243
0244 def TrackingAction(kernel, nam, shared=False):
0245 return Interface.createTrackingAction(kernel, str(nam), shared)
0246
0247
0248
0249 def SteppingAction(kernel, nam, shared=False):
0250 return Interface.createSteppingAction(kernel, str(nam), shared)
0251
0252
0253
0254 def StackingAction(kernel, nam, shared=False):
0255 return Interface.createStackingAction(kernel, str(nam), shared)
0256
0257
0258
0259 def DetectorConstruction(kernel, nam):
0260 return Interface.createDetectorConstruction(kernel, str(nam))
0261
0262
0263
0264 def PhysicsList(kernel, nam):
0265 return Interface.createPhysicsList(kernel, str(nam))
0266
0267
0268
0269 def UserInitialization(kernel, nam):
0270 return Interface.createUserInitialization(kernel, str(nam))
0271
0272
0273
0274 def SensitiveSequence(kernel, nam):
0275 return Interface.createSensDetSequence(kernel, str(nam))
0276
0277
0278
0279 def _setup(obj):
0280 def _adopt(self, action):
0281 self.__adopt(action.get())
0282 _import_class('Sim', obj)
0283 o = getattr(current, obj)
0284 o.__adopt = o.adopt
0285 o.adopt = _adopt
0286 o.add = _adopt
0287
0288
0289
0290 def _setup_callback(obj):
0291 def _adopt(self, action):
0292 self.__adopt(action.get(), action.callback())
0293 _import_class('Sim', obj)
0294 o = getattr(current, obj)
0295 o.__adopt = o.add
0296 o.add = _adopt
0297
0298
0299 _setup_callback('Geant4ActionPhase')
0300 _setup('Geant4RunActionSequence')
0301 _setup('Geant4EventActionSequence')
0302 _setup('Geant4GeneratorActionSequence')
0303 _setup('Geant4TrackingActionSequence')
0304 _setup('Geant4SteppingActionSequence')
0305 _setup('Geant4StackingActionSequence')
0306 _setup('Geant4PhysicsListActionSequence')
0307 _setup('Geant4SensDetActionSequence')
0308 _setup('Geant4DetectorConstructionSequence')
0309 _setup('Geant4UserInitializationSequence')
0310 _setup('Geant4Sensitive')
0311 _setup('Geant4ParticleHandler')
0312 _import_class('Sim', 'Geant4Vertex')
0313 _import_class('Sim', 'Geant4Particle')
0314 _import_class('Sim', 'Geant4VertexVector')
0315 _import_class('Sim', 'Geant4ParticleVector')
0316 _import_class('Sim', 'Geant4Action')
0317 _import_class('Sim', 'Geant4Filter')
0318 _import_class('Sim', 'Geant4RunAction')
0319 _import_class('Sim', 'Geant4TrackingAction')
0320 _import_class('Sim', 'Geant4StackingAction')
0321 _import_class('Sim', 'Geant4PhaseAction')
0322 _import_class('Sim', 'Geant4UserParticleHandler')
0323 _import_class('Sim', 'Geant4UserInitialization')
0324 _import_class('Sim', 'Geant4DetectorConstruction')
0325 _import_class('Sim', 'Geant4GeneratorWrapper')
0326 _import_class('Sim', 'Geant4VolumeManager')
0327 _import_class('Sim', 'Geant4Random')
0328 _import_class('CLHEP', 'HepRandom')
0329 _import_class('CLHEP', 'HepRandomEngine')
0330
0331 from ROOT import G4VPhysicsConstructor as G4VPhysicsConstructor
0332 from ROOT import G4StepLimiterPhysics as StepLimiterPhysics
0333
0334
0335
0336 def _get(self, name):
0337 a = Interface.toAction(self)
0338 ret = Interface.getProperty(a, name)
0339 if ret.status > 0:
0340 return _evalProperty(ret.data)
0341 elif hasattr(self.action, name):
0342 return _evalProperty(getattr(self.action, name))
0343 elif hasattr(a, name):
0344 return _evalProperty(getattr(a, name))
0345 msg = 'Geant4Action::GetProperty [Unhandled]: Cannot access property ' + a.name() + '.' + name
0346 raise KeyError(msg)
0347
0348
0349
0350 def _set(self, name, value):
0351 """This function is called when properties are passed to the c++ objects."""
0352 from dd4hep_base import unicode_2_string
0353 a = Interface.toAction(self)
0354 name = unicode_2_string(name)
0355 value = unicode_2_string(value)
0356 if Interface.setProperty(a, name, value):
0357 return
0358 msg = 'Geant4Action::SetProperty [Unhandled]: Cannot set ' + a.name() + '.' + name + ' = ' + value
0359 raise KeyError(msg)
0360
0361
0362
0363 def _props(obj):
0364 _import_class('Sim', obj)
0365 cl = getattr(current, obj)
0366 cl.__getattr__ = _get
0367 cl.__setattr__ = _set
0368
0369
0370 _props('FilterHandle')
0371 _props('ActionHandle')
0372 _props('PhaseActionHandle')
0373 _props('RunActionHandle')
0374 _props('EventActionHandle')
0375 _props('GeneratorActionHandle')
0376 _props('PhysicsListHandle')
0377 _props('TrackingActionHandle')
0378 _props('SteppingActionHandle')
0379 _props('StackingActionHandle')
0380 _props('DetectorConstructionHandle')
0381 _props('SensitiveHandle')
0382 _props('UserInitializationHandle')
0383 _props('Geant4ParticleHandler')
0384 _props('Geant4UserParticleHandler')
0385
0386 _props('GeneratorActionSequenceHandle')
0387 _props('RunActionSequenceHandle')
0388 _props('EventActionSequenceHandle')
0389 _props('TrackingActionSequenceHandle')
0390 _props('SteppingActionSequenceHandle')
0391 _props('StackingActionSequenceHandle')
0392 _props('DetectorConstructionSequenceHandle')
0393 _props('PhysicsListActionSequenceHandle')
0394 _props('SensDetActionSequenceHandle')
0395 _props('UserInitializationSequenceHandle')
0396
0397 _props('Geant4PhysicsListActionSequence')
0398 _import_class('Sim', 'Geant4HitData')
0399 _import_class('Sim', 'Geant4ParticleProperties')
0400
0401
0402
0403
0404
0405
0406
0407 class Geant4:
0408 """
0409 Helper object to perform stuff, which occurs very often.
0410 I am sick of typing the same over and over again.
0411 Hence, I grouped often used python fragments to this small
0412 class to re-usage.
0413
0414 \author M.Frank
0415 \version 1.0
0416 """
0417
0418 def __init__(self, kernel=None,
0419 calo='Geant4CalorimeterAction',
0420 tracker='Geant4SimpleTrackerAction'):
0421 kernel.UI = "UI"
0422 kernel.printProperties()
0423 self._kernel = kernel
0424 if kernel is None:
0425 self._kernel = Kernel()
0426 self.description = self._kernel.detectorDescription()
0427 self.sensitive_types = {}
0428 self.sensitive_types['tracker'] = tracker
0429 self.sensitive_types['calorimeter'] = calo
0430 self.sensitive_types['escape_counter'] = 'Geant4EscapeCounter'
0431
0432 def kernel(self):
0433 """
0434 Access the worker kernel object.
0435
0436 \author M.Frank
0437 """
0438 return self._kernel.worker()
0439
0440 def master(self):
0441 """
0442 Access the master kernel object.
0443
0444 \author M.Frank
0445 """
0446 return self._kernel
0447
0448 def setupUI(self, typ='csh', vis=False, ui=True, macro=None):
0449 """
0450 Configure the Geant4 command executive
0451
0452 \author M.Frank
0453 """
0454 ui_action = Action(self.master(), "Geant4UIManager/UI")
0455 if vis:
0456 ui_action.HaveVIS = True
0457 else:
0458 ui_action.HaveVIS = False
0459 if ui:
0460 ui_action.HaveUI = True
0461 else:
0462 ui_action.HaveUI = False
0463 ui_action.SessionType = typ
0464 if macro:
0465 ui_action.SetupUI = macro
0466 self.master().registerGlobalAction(ui_action)
0467 return ui_action
0468
0469 def setupCshUI(self, typ='csh', vis=False, ui=True, macro=None):
0470 """
0471 Configure the Geant4 command executive with a csh like command prompt
0472
0473 \author M.Frank
0474 """
0475 return self.setupUI(typ='csh', vis=vis, ui=ui, macro=macro)
0476
0477 def ui(self):
0478 """
0479 Access UI manager action from the kernel object
0480
0481 \author M.Frank
0482 """
0483
0484 ui_name = self.master().UI
0485 return self.master().globalAction(ui_name)
0486
0487 def registerInterruptHandler(self, signum=signal.SIGINT):
0488 """
0489 Enable interrupt handling: smooth handling of CTRL-C
0490 - Finish processing of the current event(s)
0491 - Drain the event loop
0492 - Properly finalize the job
0493
0494 \author M.Frank
0495 """
0496 return self.master().registerInterruptHandler(signum)
0497
0498 def addUserInitialization(self, worker, worker_args=None, master=None, master_args=None):
0499 """
0500 Configure Geant4 user initialization for optionasl multi-threading mode
0501
0502 \author M.Frank
0503 """
0504 init_seq = self.master().userInitialization(True)
0505 init_action = UserInitialization(self.master(), 'Geant4PythonInitialization/PyG4Init')
0506
0507 if worker:
0508 init_action.setWorkerSetup(worker, worker_args)
0509 else:
0510 raise RuntimeError('Invalid argument for Geant4 worker initialization')
0511
0512 if master:
0513 init_action.setMasterSetup(master, master_args)
0514
0515 init_seq.adopt(init_action)
0516 return init_seq, init_action
0517
0518 def detectorConstruction(self):
0519 seq = self.master().detectorConstruction(True)
0520 return seq
0521
0522 def addDetectorConstruction(self, name_type,
0523 field=None, field_args=None,
0524 geometry=None, geometry_args=None,
0525 sensitives=None, sensitives_args=None,
0526 allow_threads=False):
0527 """
0528 Configure Geant4 user initialization for optional multi-threading mode
0529
0530 \author M.Frank
0531 """
0532 init_seq = self.master().detectorConstruction(True)
0533 init_action = DetectorConstruction(self.master(), name_type)
0534
0535 if geometry:
0536 init_action.setConstructGeo(geometry, geometry_args)
0537
0538 if field:
0539 init_action.setConstructField(field, field_args)
0540
0541 if sensitives:
0542 init_action.setConstructSensitives(sensitives, sensitives_args)
0543
0544 init_seq.adopt(init_action)
0545 if allow_threads:
0546 last_action = DetectorConstruction(self.master(), "Geant4PythonDetectorConstructionLast/LastDetectorAction")
0547 init_seq.adopt(last_action)
0548
0549 return init_seq, init_action
0550
0551 def addPhaseAction(self, phase_name, factory_specification, ui=True, instance=None):
0552 """
0553 Add a new phase action to an arbitrary step.
0554
0555 \author M.Frank
0556 """
0557 if instance is None:
0558 instance = self.kernel()
0559 action = PhaseAction(instance, factory_specification)
0560 instance.phase(phase_name).add(action)
0561 if ui:
0562 action.enableUI()
0563 return action
0564
0565 def addConfig(self, factory_specification):
0566 """
0567 Add a new phase action to the 'configure' step.
0568 Called at the beginning of Geant4Exec::configure.
0569 The factory specification is the typical string "<factory_name>/<instance name>".
0570 If no instance name is specified it defaults to the factory name.
0571
0572 \author M.Frank
0573 """
0574 return self.addPhaseAction('configure', factory_specification, instance=self.master())
0575
0576 def addInit(self, factory_specification):
0577 """
0578 Add a new phase action to the 'initialize' step.
0579 Called at the beginning of Geant4Exec::initialize.
0580 The factory specification is the typical string "<factory_name>/<instance name>".
0581 If no instance name is specified it defaults to the factory name.
0582
0583 \author M.Frank
0584 """
0585 return self.addPhaseAction('initialize', factory_specification)
0586
0587 def addStart(self, factory_specification):
0588 """
0589 Add a new phase action to the 'start' step.
0590 Called at the beginning of Geant4Exec::run.
0591 The factory specification is the typical string "<factory_name>/<instance name>".
0592 If no instance name is specified it defaults to the factory name.
0593
0594 \author M.Frank
0595 """
0596 return self.addPhaseAction('start', factory_specification)
0597
0598 def addStop(self, factory_specification):
0599 """
0600 Add a new phase action to the 'stop' step.
0601 Called at the end of Geant4Exec::run.
0602 The factory specification is the typical string "<factory_name>/<instance name>".
0603 If no instance name is specified it defaults to the factory name.
0604
0605 \author M.Frank
0606 """
0607 return self.addPhaseAction('stop', factory_specification)
0608
0609 def execute(self, num_events=None):
0610 """
0611 Execute the Geant 4 program with all steps.
0612
0613 \author M.Frank
0614 """
0615 self.kernel().configure()
0616 self.kernel().initialize()
0617 if num_events:
0618 self.kernel().NumEvents = num_events
0619 self.kernel().run()
0620 self.kernel().terminate()
0621 return self
0622
0623 def printDetectors(self, **kwargs):
0624 """
0625 Scan the list of detectors and print detector name and sensitive type
0626
0627 \author M.Frank
0628 """
0629 print_path = kwargs.get('print_path')
0630 sensitive = kwargs.get('sensitive') or True
0631 non_sensitive = kwargs.get('non_sensitive')
0632 if sensitive:
0633 logger.info('+++ List of sensitive detectors:')
0634 for i in self.description.detectors():
0635 o = DetElement(i.second.ptr())
0636 sd = self.description.sensitiveDetector(str(o.name()))
0637 if sd.isValid():
0638 path = ''
0639 typ = sd.type()
0640 sdtyp = 'Unknown'
0641 if typ in self.sensitive_types:
0642 sdtyp = self.sensitive_types[typ]
0643 if print_path:
0644 path = o.path()
0645 logger.info('+++ %-32s type:%-12s --> Sensitive type: %-30s %s', o.name(), typ, sdtyp, path)
0646 if non_sensitive:
0647 logger.info('+++ List of not sensitive detector elements:')
0648 for i in self.description.detectors():
0649 o = DetElement(i.second.ptr())
0650 sd = self.description.sensitiveDetector(str(o.name()))
0651 if not sd.isValid():
0652 path = ''
0653 if print_path:
0654 path = o.path()
0655 logger.info('+++ %-32s type:non-sensitive detector %42s %s', o.name(), '', path)
0656
0657 def setupDetectors(self, **kwargs):
0658 """
0659 Scan the list of detectors and assign the proper sensitive actions
0660
0661 \author M.Frank
0662 """
0663 seq = None
0664 actions = []
0665 debug_volid = kwargs.get('debug_volid')
0666 logger.info('+++ Setting up sensitive detectors:')
0667 for i in self.description.detectors():
0668 o = DetElement(i.second.ptr())
0669 sd = self.description.sensitiveDetector(str(o.name()))
0670 if sd.isValid():
0671 typ = sd.type()
0672 sdtyp = 'Unknown'
0673 if typ in self.sensitive_types:
0674 sdtyp = self.sensitive_types[typ]
0675 seq, act = self.setupDetector(o.name(), sdtyp, collections=None, debug_volid=debug_volid)
0676 logger.info('+++ %-32s type:%-12s --> Sensitive type: %s', o.name(), typ, sdtyp)
0677 actions.append(act)
0678 continue
0679 logger.info('+++ %-32s --> UNKNOWN Sensitive type: %s', o.name(), typ)
0680 return (seq, actions)
0681
0682 def setupDetector(self, name, action, collections=None, debug_volid=False):
0683 """
0684 Setup single subdetector and assign the proper sensitive action
0685
0686 \author M.Frank
0687 """
0688
0689 sensitive_type = ""
0690 parameterDict = {}
0691 if isinstance(action, tuple) or isinstance(action, list):
0692 sensitive_type = action[0]
0693 parameterDict = action[1]
0694 else:
0695 sensitive_type = action
0696
0697 seq = SensitiveSequence(self.kernel(), 'Geant4SensDetActionSequence/' + name)
0698 seq.enableUI()
0699 acts = []
0700 if collections is None:
0701 sd = self.description.sensitiveDetector(str(name))
0702 ro = sd.readout()
0703 collections = ro.collectionNames()
0704 if len(collections) == 0:
0705 act = SensitiveAction(self.kernel(), sensitive_type + '/' + name + 'Handler', name)
0706 if debug_volid:
0707 act.DebugVolumeID = debug_volid
0708 for parameter, value in parameterDict.items():
0709 setattr(act, parameter, value)
0710 acts.append(act)
0711
0712
0713 if collections is not None:
0714 for coll in collections:
0715 params = {}
0716 if isinstance(coll, tuple) or isinstance(coll, list):
0717 if len(coll) > 2:
0718 coll_nam = str(coll[0])
0719 sensitive_type = coll[1]
0720 params = str(coll[2])
0721 elif len(coll) > 1:
0722 coll_nam = str(coll[0])
0723 sensitive_type = coll[1]
0724 else:
0725 coll_nam = str(coll[0])
0726 else:
0727 coll_nam = str(coll)
0728 act = SensitiveAction(self.kernel(), sensitive_type + '/' + coll_nam + 'Handler', name)
0729 act.CollectionName = coll_nam
0730 for parameter, value in params.items():
0731 setattr(act, parameter, value)
0732 acts.append(act)
0733
0734 for act in acts:
0735 act.enableUI()
0736 seq.add(act)
0737 if len(acts) > 1:
0738 return (seq, acts)
0739 return (seq, acts[0])
0740
0741 def setupCalorimeter(self, name, type=None, collections=None, debug_volid=False):
0742 """
0743 Setup subdetector of type 'calorimeter' and assign the proper sensitive action
0744
0745 \author M.Frank
0746 """
0747 typ = type
0748 self.description.sensitiveDetector(str(name))
0749
0750 if typ is None:
0751 typ = self.sensitive_types['calorimeter']
0752 return self.setupDetector(name, typ, collections=collections, debug_volid=debug_volid)
0753
0754 def setupTracker(self, name, type=None, collections=None, debug_volid=False):
0755 """
0756 Setup subdetector of type 'tracker' and assign the proper sensitive action
0757
0758 \author M.Frank
0759 """
0760 typ = type
0761 self.description.sensitiveDetector(str(name))
0762
0763 if typ is None:
0764 typ = self.sensitive_types['tracker']
0765 return self.setupDetector(name, typ, collections=collections, debug_volid=debug_volid)
0766
0767 def _private_setupField(self, field, stepper, equation, prt):
0768 import g4units
0769 field.stepper = stepper
0770 field.equation = equation
0771 field.eps_min = 5e-05
0772 field.eps_max = 0.001
0773 field.min_chord_step = 0.01 * g4units.mm
0774 field.delta_chord = 0.25 * g4units.mm
0775 field.delta_intersection = 0.001 * g4units.mm
0776 field.delta_one_step = 0.01 * g4units.mm
0777 field.largest_step = 1000 * g4units.m
0778 if prt:
0779 logger.info('+++++> %s %s %s %s ', field.name, '-> stepper = ', str(field.stepper), '')
0780 logger.info('+++++> %s %s %s %s ', field.name, '-> equation = ', str(field.equation), '')
0781 logger.info('+++++> %s %s %s %s ', field.name, '-> eps_min = ', str(field.eps_min), '')
0782 logger.info('+++++> %s %s %s %s ', field.name, '-> eps_max = ', str(field.eps_max), '')
0783 logger.info('+++++> %s %s %s %s ', field.name, '-> delta_chord = ', str(field.delta_chord), '[mm]')
0784 logger.info('+++++> %s %s %s %s ', field.name, '-> min_chord_step = ', str(field.min_chord_step), '[mm]')
0785 logger.info('+++++> %s %s %s %s ', field.name, '-> delta_one_step = ', str(field.delta_one_step), '[mm]')
0786 logger.info('+++++> %s %s %s %s ', field.name, '-> delta_intersection = ', str(field.delta_intersection), '[mm]')
0787 logger.info('+++++> %s %s %s %s ', field.name, '-> largest_step = ', str(field.largest_step), '[mm]')
0788 return field
0789
0790 def setupTrackingFieldMT(self, name='MagFieldTrackingSetup',
0791 stepper='ClassicalRK4', equation='Mag_UsualEqRhs', prt=False):
0792 seq, fld = self.addDetectorConstruction("Geant4FieldTrackingConstruction/" + name)
0793 self._private_setupField(fld, stepper, equation, prt)
0794 return (seq, fld)
0795
0796 def setupTrackingField(self, name='MagFieldTrackingSetup',
0797 stepper='ClassicalRK4', equation='Mag_UsualEqRhs', prt=False):
0798 field = self.addConfig('Geant4FieldTrackingSetupAction/' + name)
0799 self._private_setupField(field, stepper, equation, prt)
0800 return field
0801
0802
0803 def setupPhysics(self, name):
0804 phys = self.master().physicsList()
0805 phys.extends = name
0806 phys.decays = True
0807 phys.enableUI()
0808 phys.dump()
0809 return phys
0810
0811
0812 def addPhysics(self, name):
0813 phys = self.master().physicsList()
0814 opt = PhysicsList(self.master(), name)
0815 opt.enableUI()
0816 phys.adopt(opt)
0817 return opt
0818
0819 def setupGun(self, name, particle, energy, typ="Geant4ParticleGun", isotrop=True,
0820 multiplicity=1, position=(0.0, 0.0, 0.0), register=True, **args):
0821 gun = GeneratorAction(self.kernel(), typ + "/" + name, True)
0822 for i in args.items():
0823 setattr(gun, i[0], i[1])
0824 gun.Energy = energy
0825 gun.particle = particle
0826 gun.multiplicity = multiplicity
0827 gun.position = position
0828 gun.isotrop = isotrop
0829 gun.enableUI()
0830 if register:
0831 self.kernel().generatorAction().add(gun)
0832 return gun
0833
0834 def setupROOTOutput(self, name, output, mc_truth=True):
0835 """
0836 Configure ROOT output for the simulated events
0837
0838 \author M.Frank
0839 """
0840 evt_root = EventAction(self.kernel(), 'Geant4Output2ROOT/' + name, True)
0841 evt_root.HandleMCTruth = mc_truth
0842 evt_root.Control = True
0843 if not output.endswith('.root'):
0844 output = output + '.root'
0845 evt_root.Output = output
0846 evt_root.enableUI()
0847 self.kernel().eventAction().add(evt_root)
0848 return evt_root
0849
0850 def setupLCIOOutput(self, name, output):
0851 """
0852 Configure LCIO output for the simulated events
0853
0854 \author M.Frank
0855 """
0856 evt_lcio = EventAction(self.kernel(), 'Geant4Output2LCIO/' + name, True)
0857 evt_lcio.Control = True
0858 evt_lcio.Output = output
0859 evt_lcio.enableUI()
0860 self.kernel().eventAction().add(evt_lcio)
0861 return evt_lcio
0862
0863 def setupEDM4hepOutput(self, name, output):
0864 """Configure EDM4hep root output for the simulated events."""
0865 evt_edm4hep = EventAction(self.kernel(), 'Geant4Output2EDM4hep/' + name, True)
0866 evt_edm4hep.Control = True
0867 evt_edm4hep.Output = output
0868 evt_edm4hep.enableUI()
0869 self.kernel().eventAction().add(evt_edm4hep)
0870 return evt_edm4hep
0871
0872 def buildInputStage(self, generator_input_modules, output_level=None, have_mctruth=True):
0873 """
0874 Generic build of the input stage with multiple input modules.
0875
0876 Actions executed are:
0877 1) Register Generation initialization action
0878 2) Append all modules to build the complete input record
0879 These modules are readers/particle sources, boosters and/or smearing actions.
0880 3) Merge all existing interaction records
0881 4) Add the MC truth handler
0882
0883 \author M.Frank
0884 """
0885 ga = self.kernel().generatorAction()
0886
0887 gen = GeneratorAction(self.kernel(), "Geant4GeneratorActionInit/GenerationInit")
0888 if output_level is not None:
0889 gen.OutputLevel = output_level
0890 ga.adopt(gen)
0891
0892
0893
0894 for gen in generator_input_modules:
0895 gen.enableUI()
0896 if output_level is not None:
0897 gen.OutputLevel = output_level
0898 ga.adopt(gen)
0899
0900
0901 gen = GeneratorAction(self.kernel(), "Geant4InteractionMerger/InteractionMerger")
0902 gen.enableUI()
0903 if output_level is not None:
0904 gen.OutputLevel = output_level
0905 ga.adopt(gen)
0906
0907
0908 if have_mctruth:
0909 gen = GeneratorAction(self.kernel(), "Geant4PrimaryHandler/PrimaryHandler")
0910 gen.RejectPDGs = "{1,2,3,4,5,6,21,23,24}"
0911 gen.enableUI()
0912 if output_level is not None:
0913 gen.OutputLevel = output_level
0914 ga.adopt(gen)
0915
0916 return self
0917
0918 def run(self):
0919 """
0920 Execute the main Geant4 action
0921 \author M.Frank
0922 """
0923 from ROOT import PyDDG4
0924 PyDDG4.run(self.master().get())
0925 return self
0926
0927
0928
0929
0930 Simple = Geant4
0931
0932
0933
0934 def import_geant4_class(class_name, header=None):
0935 try:
0936 from ROOT import gInterpreter
0937 if not header:
0938 header = class_name + '.hh'
0939 ret = gInterpreter.ProcessLine(f'#include <{header}>')
0940 if 0 == ret:
0941 g4_class = getattr(ROOT, class_name)
0942 if g4_class:
0943 logger.warning(f'+++ Successfully imported Geant4 class {class_name} from header {class_name}.hh')
0944 return g4_class
0945 except Exception:
0946 pass
0947 logger.error(f'+++ FAILED to import class Geant4 class {class_name}')
0948 return None
0949
0950
0951
0952 class Geant4_class_loader:
0953 all_classes = {}
0954
0955 def __init__(self):
0956 self.gbl = globals()
0957
0958 def get_class(self, name, header=None):
0959 clazz = Geant4_class_loader.all_classes.get(name)
0960 if not clazz:
0961 clazz = import_geant4_class(class_name=name, header=header)
0962 if clazz:
0963 Geant4_class_loader.all_classes[name] = clazz
0964 return clazz
0965
0966 def __getattr__(self, name):
0967 return self.get_class(name)
0968
0969
0970
0971
0972 geant4 = Geant4_class_loader()