Newer
Older
# ==========================================================================
# --------------------------------------------------------------------------
# Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
Markus Frank
committed
# All rights reserved.
#
# For the licensing terms see $DD4hepINSTALL/LICENSE.
# For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
#
# ==========================================================================
from __future__ import absolute_import, unicode_literals
import g4units as G4Units
from dd4hep_base import std, std_vector, std_list, std_map, std_pair
Markus Frank
committed
import logging
Marko Petric
committed
logger = logging.getLogger(__name__)
# Try to load libglapi to avoid issues with TLS Static
# Turn off all errors from ROOT about the library missing
orgLevel = ROOT.gErrorIgnoreLevel
gSystem.Load("libglapi")
gSystem.SetDynamicPath(os.environ['DD4HEP_LIBRARY_PATH'])
os.environ['DYLD_LIBRARY_PATH'] = os.pathsep.join([os.environ['DD4HEP_LIBRARY_PATH'],
os.environ.get('DYLD_LIBRARY_PATH', '')]).strip(os.pathsep)
result = gSystem.Load("libDDG4Plugins")
if result < 0:
raise Exception('DDG4.py: Failed to load the DDG4 library libDDG4Plugins: ' + gSystem.GetErrorStr())
# We are nearly there ....
current = __import__(__name__)
def _import_class(ns, nam):
scope = getattr(current, ns)
setattr(current, nam, getattr(scope, nam))
# ---------------------------------------------------------------------------
except Exception as X:
logger.error('+--%-100s--+', 100 * '-')
logger.error('| %-100s |', 'Failed to load DDG4 library:')
logger.error('| %-100s |', str(X))
logger.error('+--%-100s--+', 100 * '-')
exit(1)
Markus Frank
committed
Kernel = Sim.KernelHandle
Interface = Sim.Geant4ActionCreation
Detector = Core.Detector
# ---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
def importConstants(description, namespace=None, debug=False):
"""
Import the Detector constants into the DDG4 namespace
"""
if namespace is not None and not hasattr(current, namespace):
m = imp.new_module('DDG4.' + namespace)
setattr(current, namespace, m)
Markus Frank
committed
cnt = 0
num = 0
todo = {}
if c.second.dataType == 'string':
strings[c.first] = c.second.GetTitle()
else:
todo[c.first] = c.second.GetTitle().replace('(int)', '')
while len(todo) and cnt < 100:
Markus Frank
committed
cnt = cnt + 1
if cnt == 100:
logger.error('%s %d out of %d %s "%s": [%s]\n+++ %s',
'+++ FAILED to import',
len(todo), len(todo) + num,
'global values into namespace',
ns.__name__, 'Try to continue anyway', 100 * '=')
for k, v in todo.items():
if not hasattr(ns, k):
logger.error('+++ FAILED to import: "' + k + '" = "' + str(v) + '"')
logger.info('+++ %s', 100 * '=')
for k, v in list(todo.items()):
if not hasattr(ns, k):
Markus Frank
committed
val = evaluator.evaluate(v)
status = evaluator.status()
if status == 0:
evaluator.setVariable(k, val)
setattr(ns, k, val)
if debug:
logger.info('Imported global value: "' + k + '" = "' + str(val) + '" into namespace' + ns.__name__)
Markus Frank
committed
del todo[k]
num = num + 1
if cnt < 100:
logger.info('+++ Imported %d global values to namespace:%s', num, ns.__name__,)
# ---------------------------------------------------------------------------
Markus Frank
committed
self.get().registerGlobalAction(Interface.toAction(action))
self.get().registerGlobalFilter(Interface.toAction(filter))
# ---------------------------------------------------------------------------
elif hasattr(self.get(), name):
return getattr(self.get(), name)
elif hasattr(self, name):
return getattr(self, name)
msg = 'Geant4Kernel::GetProperty [Unhandled]: Cannot access Kernel.' + name
# ---------------------------------------------------------------------------
if Interface.setPropertyKernel(self.get(), name, str(value)):
msg = 'Geant4Kernel::SetProperty [Unhandled]: Cannot set Kernel.' + name + ' = ' + str(value)
# ---------------------------------------------------------------------------
def _kernel_phase(self, name): return self.addSimplePhase(str(name), False)
# ---------------------------------------------------------------------------
def _kernel_worker(self): return Kernel(self.get().createWorker())
# ---------------------------------------------------------------------------
def _kernel_terminate(self): return self.get().terminate()
# ---------------------------------------------------------------------------
Markus Frank
committed
Kernel.phase = _kernel_phase
Kernel.registerGlobalAction = _registerGlobalAction
Kernel.registerGlobalFilter = _registerGlobalFilter
Markus Frank
committed
Kernel.createWorker = _kernel_worker
Kernel.__getattr__ = _getKernelProperty
Kernel.__setattr__ = _setKernelProperty
# ---------------------------------------------------------------------------
Markus Frank
committed
ActionHandle = Sim.ActionHandle
# ---------------------------------------------------------------------------
def SensitiveAction(kernel, nam, det, shared=False):
return Interface.createSensitive(kernel, str(nam), str(det), shared)
# ---------------------------------------------------------------------------
def Action(kernel, nam, shared=False):
return Interface.createAction(kernel, str(nam), shared)
# ---------------------------------------------------------------------------
def Filter(kernel, nam, shared=False):
return Interface.createFilter(kernel, str(nam), shared)
# ---------------------------------------------------------------------------
def PhaseAction(kernel, nam, shared=False):
return Interface.createPhaseAction(kernel, str(nam), shared)
# ---------------------------------------------------------------------------
def RunAction(kernel, nam, shared=False):
return Interface.createRunAction(kernel, str(nam), shared)
# ---------------------------------------------------------------------------
def EventAction(kernel, nam, shared=False):
return Interface.createEventAction(kernel, str(nam), shared)
# ---------------------------------------------------------------------------
def GeneratorAction(kernel, nam, shared=False):
return Interface.createGeneratorAction(kernel, str(nam), shared)
# ---------------------------------------------------------------------------
def TrackingAction(kernel, nam, shared=False):
return Interface.createTrackingAction(kernel, str(nam), shared)
# ---------------------------------------------------------------------------
def SteppingAction(kernel, nam, shared=False):
return Interface.createSteppingAction(kernel, str(nam), shared)
# ---------------------------------------------------------------------------
def StackingAction(kernel, nam, shared=False):
return Interface.createStackingAction(kernel, str(nam), shared)
# ---------------------------------------------------------------------------
def DetectorConstruction(kernel, nam):
return Interface.createDetectorConstruction(kernel, str(nam))
# ---------------------------------------------------------------------------
def PhysicsList(kernel, nam):
return Interface.createPhysicsList(kernel, str(nam))
# ---------------------------------------------------------------------------
Markus Frank
committed
def UserInitialization(kernel, nam):
return Interface.createUserInitialization(kernel, str(nam))
# ---------------------------------------------------------------------------
Markus Frank
committed
def SensitiveSequence(kernel, nam):
return Interface.createSensDetSequence(kernel, str(nam))
# ---------------------------------------------------------------------------
def _adopt(self, action): self.__adopt(action.get())
_import_class('Sim', obj)
o = getattr(current, obj)
setattr(o, '__adopt', getattr(o, 'adopt'))
setattr(o, 'adopt', _adopt)
setattr(o, 'add', _adopt)
Markus Frank
committed
def _setup_callback(obj):
def _adopt(self, action): self.__adopt(action.get(), action.callback())
_import_class('Sim', obj)
o = getattr(current, obj)
setattr(o, '__adopt', getattr(o, 'add'))
setattr(o, 'add', _adopt)
Markus Frank
committed
# ---------------------------------------------------------------------------
Markus Frank
committed
_setup_callback('Geant4ActionPhase')
_setup('Geant4RunActionSequence')
_setup('Geant4EventActionSequence')
_setup('Geant4GeneratorActionSequence')
_setup('Geant4TrackingActionSequence')
_setup('Geant4SteppingActionSequence')
_setup('Geant4StackingActionSequence')
_setup('Geant4PhysicsListActionSequence')
_setup('Geant4SensDetActionSequence')
Markus Frank
committed
_setup('Geant4DetectorConstructionSequence')
_setup('Geant4UserInitializationSequence')
_import_class('Sim', 'Geant4Vertex')
_import_class('Sim', 'Geant4Particle')
_import_class('Sim', 'Geant4VertexVector')
_import_class('Sim', 'Geant4ParticleVector')
_import_class('Sim', 'Geant4Action')
_import_class('Sim', 'Geant4Filter')
_import_class('Sim', 'Geant4RunAction')
_import_class('Sim', 'Geant4TrackingAction')
_import_class('Sim', 'Geant4StackingAction')
_import_class('Sim', 'Geant4PhaseAction')
_import_class('Sim', 'Geant4UserParticleHandler')
_import_class('Sim', 'Geant4UserInitialization')
_import_class('Sim', 'Geant4DetectorConstruction')
_import_class('Sim', 'Geant4GeneratorWrapper')
_import_class('Sim', 'Geant4Random')
_import_class('CLHEP', 'HepRandom')
_import_class('CLHEP', 'HepRandomEngine')
# ---------------------------------------------------------------------------
if ret.status > 0:
return ret.data
elif hasattr(self.action, name):
return getattr(self.action, name)
elif hasattr(a, name):
return getattr(a, name)
# elif hasattr(self,name):
# traceback.print_stack()
msg = 'Geant4Action::GetProperty [Unhandled]: Cannot access property ' + a.name() + '.' + name
def _set(self, name, value):
a = Interface.toAction(self)
msg = 'Geant4Action::SetProperty [Unhandled]: Cannot set ' + a.name() + '.' + name + ' = ' + str(value)
_import_class('Sim', obj)
cl = getattr(current, obj)
cl.__getattr__ = _get
cl.__setattr__ = _set
_props('FilterHandle')
_props('ActionHandle')
Markus Frank
committed
_props('PhaseActionHandle')
_props('RunActionHandle')
_props('EventActionHandle')
_props('GeneratorActionHandle')
_props('PhysicsListHandle')
_props('TrackingActionHandle')
_props('SteppingActionHandle')
_props('StackingActionHandle')
Markus Frank
committed
_props('DetectorConstructionHandle')
Markus Frank
committed
_props('UserInitializationHandle')
_props('Geant4ParticleHandler')
_props('Geant4UserParticleHandler')
_props('GeneratorActionSequenceHandle')
_props('RunActionSequenceHandle')
_props('EventActionSequenceHandle')
_props('TrackingActionSequenceHandle')
_props('SteppingActionSequenceHandle')
_props('StackingActionSequenceHandle')
Markus Frank
committed
_props('DetectorConstructionSequenceHandle')
_props('PhysicsListActionSequenceHandle')
_props('SensDetActionSequenceHandle')
Markus Frank
committed
_props('UserInitializationSequenceHandle')
_props('Geant4PhysicsListActionSequence')
class Geant4:
"""
Helper object to perform stuff, which occurs very often.
I am sick of typing the same over and over again.
Hence, I grouped often used python fragments to this small
class to re-usage.
Markus Frank
committed
def __init__(self, kernel=None,
calo='Geant4CalorimeterAction',
tracker='Geant4SimpleTrackerAction'):
kernel.UI = "UI"
kernel.printProperties()
Markus Frank
committed
self._kernel = kernel
Markus Frank
committed
self._kernel = Kernel()
self.description = self._kernel.detectorDescription()
Markus Frank
committed
self.sensitive_types = {}
self.sensitive_types['tracker'] = tracker
self.sensitive_types['calorimeter'] = calo
self.sensitive_types['escape_counter'] = 'Geant4EscapeCounter'
Markus Frank
committed
def kernel(self):
Access the worker kernel object.
\author M.Frank
Markus Frank
committed
def master(self):
def setupUI(self, typ='csh', vis=False, ui=True, macro=None):
"""
Configure the Geant4 command executive
\author M.Frank
"""
ui_action = Action(self.master(), "Geant4UIManager/UI")
if vis:
ui_action.HaveVIS = True
else:
ui_action.HaveVIS = False
if ui:
ui_action.HaveUI = True
else:
ui_action.HaveUI = False
ui_action.SessionType = typ
if macro:
ui_action.SetupUI = macro
Markus Frank
committed
self.master().registerGlobalAction(ui_action)
return ui_action
def setupCshUI(self, typ='csh', vis=False, ui=True, macro=None):
Markus Frank
committed
"""
Configure the Geant4 command executive with a csh like command prompt
Markus Frank
committed
Markus Frank
committed
"""
return self.setupUI(typ='csh', vis=vis, ui=ui, macro=macro)
Markus Frank
committed
def addUserInitialization(self, worker, worker_args=None, master=None, master_args=None):
"""
Configure Geant4 user initialization for optionasl multi-threading mode
\author M.Frank
"""
Markus Frank
committed
import sys
init_seq = self.master().userInitialization(True)
init_action = UserInitialization(self.master(), 'Geant4PythonInitialization/PyG4Init')
Markus Frank
committed
#
if worker:
init_action.setWorkerSetup(worker, worker_args)
else:
raise RuntimeError('Invalid argument for Geant4 worker initialization')
Markus Frank
committed
#
if master:
Markus Frank
committed
#
init_seq.adopt(init_action)
Markus Frank
committed
def detectorConstruction(self):
seq = self.master().detectorConstruction(True)
return seq
def addDetectorConstruction(self, name_type,
field=None, field_args=None,
geometry=None, geometry_args=None,
Markus Frank
committed
sensitives=None, sensitives_args=None,
allow_threads=False):
"""
Configure Geant4 user initialization for optionasl multi-threading mode
\author M.Frank
"""
Markus Frank
committed
init_seq = self.master().detectorConstruction(True)
init_action = DetectorConstruction(self.master(), name_type)
Markus Frank
committed
#
if geometry:
Markus Frank
committed
#
if field:
Markus Frank
committed
#
if sensitives:
init_action.setConstructSensitives(sensitives, sensitives_args)
Markus Frank
committed
#
init_seq.adopt(init_action)
if allow_threads:
last_action = DetectorConstruction(self.master(), "Geant4PythonDetectorConstructionLast/LastDetectorAction")
Markus Frank
committed
init_seq.adopt(last_action)
Markus Frank
committed
def addPhaseAction(self, phase_name, factory_specification, ui=True, instance=None):
"""
Add a new phase action to an arbitrary step.
\author M.Frank
"""
Markus Frank
committed
if instance is None:
instance = self.kernel()
action = PhaseAction(instance, factory_specification)
Markus Frank
committed
instance.phase(phase_name).add(action)
Markus Frank
committed
return action
def addConfig(self, factory_specification):
"""
Add a new phase action to the 'configure' step.
Called at the beginning of Geant4Exec::configure.
The factory specification is the typical string "<factory_name>/<instance name>".
If no instance name is specified it defaults to the factory name.
\author M.Frank
"""
return self.addPhaseAction('configure', factory_specification, instance=self.master())
Markus Frank
committed
def addInit(self, factory_specification):
"""
Add a new phase action to the 'initialize' step.
Called at the beginning of Geant4Exec::initialize.
The factory specification is the typical string "<factory_name>/<instance name>".
If no instance name is specified it defaults to the factory name.
\author M.Frank
"""
return self.addPhaseAction('initialize', factory_specification)
Markus Frank
committed
def addStart(self, factory_specification):
"""
Add a new phase action to the 'start' step.
Called at the beginning of Geant4Exec::run.
The factory specification is the typical string "<factory_name>/<instance name>".
If no instance name is specified it defaults to the factory name.
\author M.Frank
"""
return self.addPhaseAction('start', factory_specification)
Markus Frank
committed
def addStop(self, factory_specification):
"""
Add a new phase action to the 'stop' step.
Called at the end of Geant4Exec::run.
The factory specification is the typical string "<factory_name>/<instance name>".
If no instance name is specified it defaults to the factory name.
\author M.Frank
"""
return self.addPhaseAction('stop', factory_specification)
Markus Frank
committed
"""
Execute the Geant 4 program with all steps.
\author M.Frank
"""
Markus Frank
committed
self.kernel().configure()
self.kernel().initialize()
self.kernel().run()
self.kernel().terminate()
Marko Petric
committed
logger.info('+++ List of sensitive detectors:')
Markus Frank
committed
o = DetElement(i.second.ptr())
sd = self.description.sensitiveDetector(str(o.name()))
Markus Frank
committed
typ = sd.type()
logger.info('+++ %-32s type:%-12s --> Sensitive type: %s', o.name(), typ, sdtyp)
def setupSensitiveSequencer(self, name, action):
sensitive_type = action[0]
else:
sensitive_type = action
seq = SensitiveSequence(self.kernel(), 'Geant4SensDetActionSequence/' + name)
seq.enableUI()
return seq
def setupDetector(self, name, action, collections=None):
# fg: allow the action to be a tuple with parameter dictionary
Frank Gaede
committed
sensitive_type = ""
parameterDict = {}
if isinstance(action, tuple) or isinstance(action, list):
Frank Gaede
committed
sensitive_type = action[0]
parameterDict = action[1]
else:
sensitive_type = action
seq = SensitiveSequence(self.kernel(), 'Geant4SensDetActionSequence/' + name)
acts = []
if collections is None:
sd = self.description.sensitiveDetector(str(name))
ro = sd.readout()
collections = ro.collectionNames()
if len(collections) == 0:
act = SensitiveAction(self.kernel(), sensitive_type + '/' + name + 'Handler', name)
for parameter, value in six.iteritems(parameterDict):
acts.append(act)
# Work down the collections if present
if collections is not None:
for coll in collections:
params = {}
if isinstance(coll, tuple) or isinstance(coll, list):
if len(coll) > 2:
coll_nam = coll[0]
sensitive_type = coll[1]
params = coll[2]
coll_nam = coll[0]
sensitive_type = coll[1]
else:
coll_nam = coll[0]
else:
coll_nam = coll
act = SensitiveAction(self.kernel(), sensitive_type + '/' + coll_nam + 'Handler', name)
act.CollectionName = coll_nam
for parameter, value in six.iteritems(params):
acts.append(act)
for act in acts:
act.enableUI()
seq.add(act)
if len(acts) > 1:
return (seq, acts)
return (seq, acts[0])
def setupCalorimeter(self, name, type=None, collections=None):
sd = self.description.sensitiveDetector(str(name))
# sd.setType('calorimeter')
if type is None:
type = self.sensitive_types['calorimeter']
return self.setupDetector(name, type, collections)
def setupTracker(self, name, type=None, collections=None):
sd = self.description.sensitiveDetector(str(name))
# sd.setType('tracker')
if type is None:
type = self.sensitive_types['tracker']
return self.setupDetector(name, type, collections)
Markus Frank
committed
def _private_setupField(self, field, stepper, equation, prt):
field.stepper = stepper
field.equation = equation
field.eps_min = 5e-05 * SystemOfUnits.mm
field.eps_max = 0.001 * SystemOfUnits.mm
field.min_chord_step = 0.01 * SystemOfUnits.mm
field.delta_chord = 0.25 * SystemOfUnits.mm
field.delta_intersection = 0.001 * SystemOfUnits.mm
field.delta_one_step = 0.01 * SystemOfUnits.mm
field.largest_step = 1000 * SystemOfUnits.m
logger.info('+++++> %s %s %s %s ', field.name, '-> stepper = ', str(field.stepper), '')
logger.info('+++++> %s %s %s %s ', field.name, '-> equation = ', str(field.equation), '')
logger.info('+++++> %s %s %s %s ', field.name, '-> eps_min = ', str(field.eps_min), '[mm]')
logger.info('+++++> %s %s %s %s ', field.name, '-> eps_max = ', str(field.eps_max), '[mm]')
logger.info('+++++> %s %s %s %s ', field.name, '-> delta_chord = ', str(field.delta_chord), '[mm]')
logger.info('+++++> %s %s %s %s ', field.name, '-> min_chord_step = ', str(field.min_chord_step), '[mm]')
logger.info('+++++> %s %s %s %s ', field.name, '-> delta_one_step = ', str(field.delta_one_step), '[mm]')
logger.info('+++++> %s %s %s %s ', field.name, '-> delta_intersection = ', str(field.delta_intersection), '[mm]')
logger.info('+++++> %s %s %s %s ', field.name, '-> largest_step = ', str(field.largest_step), '[mm]')
Markus Frank
committed
return field
def setupTrackingFieldMT(self, name='MagFieldTrackingSetup', stepper='ClassicalRK4', equation='Mag_UsualEqRhs', prt=False):
seq, fld = self.addDetectorConstruction("Geant4FieldTrackingConstruction/" + name)
Markus Frank
committed
self._private_setupField(fld, stepper, equation, prt)
Markus Frank
committed
def setupTrackingField(self, name='MagFieldTrackingSetup', stepper='ClassicalRK4', equation='Mag_UsualEqRhs', prt=False):
field = self.addConfig('Geant4FieldTrackingSetupAction/' + name)
Markus Frank
committed
self._private_setupField(field, stepper, equation, prt)
Markus Frank
committed
phys = self.master().physicsList()
phys.enableUI()
phys.dump()
return phys
def addPhysics(self, name):
phys = self.master().physicsList()
opt.enableUI()
phys.adopt(opt)
return opt
def setupGun(self, name, particle, energy, isotrop=True, multiplicity=1, position=(0.0, 0.0, 0.0), register=True, **args):
gun = GeneratorAction(self.kernel(), "Geant4ParticleGun/" + name, True)
setattr(gun, i[0], i[1])
gun.energy = energy
gun.particle = particle
gun.multiplicity = multiplicity
if register:
self.kernel().generatorAction().add(gun)
def setupROOTOutput(self, name, output, mc_truth=True):
"""
Configure ROOT output for the simulated events
\author M.Frank
"""
evt_root = EventAction(self.kernel(), 'Geant4Output2ROOT/' + name, True)
Markus Frank
committed
if not output.endswith('.root'):
output = output + '.root'
evt_root.Output = output
Markus Frank
committed
self.kernel().eventAction().add(evt_root)
Markus Frank
committed
"""
Configure LCIO output for the simulated events
\author M.Frank
"""
evt_lcio = EventAction(self.kernel(), 'Geant4Output2LCIO/' + name, True)
Markus Frank
committed
self.kernel().eventAction().add(evt_lcio)
def buildInputStage(self, generator_input_modules, output_level=None, have_mctruth=True):
"""
Generic build of the input stage with multiple input modules.
Actions executed are:
1) Register Generation initialization action
2) Append all modules to build the complete input record
These modules are readers/particle sources, boosters and/or smearing actions.
3) Merge all existing interaction records
4) Add the MC truth handler
\author M.Frank
"""
Markus Frank
committed
ga = self.kernel().generatorAction()
# Register Generation initialization action
gen = GeneratorAction(self.kernel(), "Geant4GeneratorActionInit/GenerationInit")
if output_level is not None:
gen.OutputLevel = output_level
ga.adopt(gen)
# Now append all modules to build the complete input record
# These modules are readers/particle sources, boosters and/or smearing actions
for gen in generator_input_modules:
gen.enableUI()
if output_level is not None:
gen.OutputLevel = output_level
ga.adopt(gen)
# Merge all existing interaction records
gen = GeneratorAction(self.kernel(), "Geant4InteractionMerger/InteractionMerger")
gen.enableUI()
if output_level is not None:
gen.OutputLevel = output_level
ga.adopt(gen)
# Finally generate Geant4 primaries
if have_mctruth:
gen = GeneratorAction(self.kernel(), "Geant4PrimaryHandler/PrimaryHandler")
gen.RejectPDGs = "{1,2,3,4,5,6,21,23,24}"
gen.enableUI()
if output_level is not None:
gen.OutputLevel = output_level
ga.adopt(gen)
# Puuuhh! All done.
return self
Markus Frank
committed
def run(self):
"""
Execute the main Geant4 action
\author M.Frank
"""
Markus Frank
committed
from ROOT import PyDDG4
PyDDG4.run(self.master().get())
Markus Frank
committed
Markus Frank
committed
Simple = Geant4