Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:17:28

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