File indexing completed on 2025-07-01 07:55:25
0001 """Helper object for physicslist properties"""
0002
0003 import os
0004
0005 from DDSim.Helper.ConfigHelper import ConfigHelper
0006 from g4units import mm
0007 import logging
0008
0009 logger = logging.getLogger(__name__)
0010
0011
0012 class Physics(ConfigHelper):
0013 """Configuration for the PhysicsList and Monte Carlo particle selection."""
0014
0015 def __init__(self):
0016 super(Physics, self).__init__()
0017 self._rangecut = 0.7 * mm
0018 self._list = "FTFP_BERT"
0019 self._decays = False
0020 self._pdgfile = None
0021 self._rejectPDGs = {1, 2, 3, 4, 5, 6,
0022 21, 23, 24, 25,
0023 1103,
0024 2101, 2103, 2203,
0025 3101, 3103, 3201, 3203, 3303,
0026 4101, 4103, 4201, 4203, 4301, 4303, 4403,
0027 5101, 5103, 5201, 5203, 5301, 5303, 5401, 5403, 5503}
0028 self._zeroTimePDGs = {11, 13, 15, 17}
0029 self._alternativeDecayStatuses = set()
0030 self._alternativeStableStatuses = set()
0031 self._userFunctions = []
0032 self._closeProperties()
0033 Physics.__doc__ += "\n\n" + self.setupUserPhysics.__doc__
0034
0035 @property
0036 def rejectPDGs(self):
0037 """Set of PDG IDs that will not be passed from the input record to Geant4.
0038
0039 Quarks, gluons and W's Z's etc should not be treated by Geant4
0040 """
0041 return self._rejectPDGs
0042
0043 @rejectPDGs.setter
0044 def rejectPDGs(self, val):
0045 self._rejectPDGs = self.makeSet(val)
0046
0047 @property
0048 def zeroTimePDGs(self):
0049 """Set of PDG IDs for particles that should not be passed to Geant4 if their properTime is 0.
0050
0051 The properTime of 0 indicates a documentation to add FSR to a lepton for example.
0052 """
0053 return self._zeroTimePDGs
0054
0055 @zeroTimePDGs.setter
0056 def zeroTimePDGs(self, val):
0057 self._zeroTimePDGs = self.makeSet(val)
0058
0059 @property
0060 def alternativeDecayStatuses(self):
0061 """Set of Generator Statuses that are used to mark unstable particles that should decay inside of Geant4.
0062 """
0063 return self._alternativeDecayStatuses
0064
0065 @alternativeDecayStatuses.setter
0066 def alternativeDecayStatuses(self, val):
0067 self._alternativeDecayStatuses = self.makeSet(val)
0068
0069 @property
0070 def alternativeStableStatuses(self):
0071 """Set of Generator Statuses that are used to mark stable particles that should be forwarded to Geant4.
0072 """
0073 return self._alternativeStableStatuses
0074
0075 @alternativeStableStatuses.setter
0076 def alternativeStableStatuses(self, val):
0077 self._alternativeStableStatuses = self.makeSet(val)
0078
0079 @property
0080 def rangecut(self):
0081 """ The global geant4 rangecut for secondary production
0082
0083 Default is 0.7 mm as is the case in geant4 10
0084
0085 To disable this plugin and be absolutely sure to use the Geant4 default range cut use "None"
0086
0087 Set printlevel to DEBUG to see a printout of all range cuts,
0088 but this only works if range cut is not "None"
0089 """
0090 return self._rangecut
0091
0092 @rangecut.setter
0093 def rangecut(self, val):
0094 if val is None:
0095 self._rangecut = None
0096 return
0097 if isinstance(val, str):
0098 if val == "None":
0099 self._rangecut = None
0100 return
0101 self._rangecut = val
0102
0103 @property
0104 def pdgfile(self):
0105 """ location of particle.tbl file containing extra particles and their lifetime information
0106
0107 For example in $DD4HEP/examples/DDG4/examples/particle.tbl
0108 """
0109 return self._pdgfile
0110
0111 @pdgfile.setter
0112 def pdgfile(self, val):
0113 if not val:
0114 self._pdgfile = None
0115 return
0116 if not os.path.exists(val):
0117 raise RuntimeError("PDGFile: %s not found" % os.path.abspath(val))
0118 self._pdgfile = os.path.abspath(val)
0119
0120 @property
0121 def decays(self):
0122 """If true, add decay processes for all particles.
0123
0124 Only enable when creating a physics list not based on an existing Geant4 list!
0125 """
0126 return self._decays
0127
0128 @decays.setter
0129 def decays(self, val):
0130 self._decays = val
0131
0132 @property
0133 def list(self):
0134 """The name of the Geant4 Physics list."""
0135 return self._list
0136
0137 @list.setter
0138 def list(self, val):
0139 self._list = val
0140
0141 def setupPhysics(self, kernel, name=None):
0142 seq = kernel.physicsList()
0143 seq.extends = name if name is not None else self.list
0144 seq.decays = self.decays
0145 seq.enableUI()
0146 seq.dump()
0147
0148 from DDG4 import PhysicsList
0149
0150
0151 if self.pdgfile:
0152 seq = kernel.physicsList()
0153 part = PhysicsList(kernel, 'Geant4ExtraParticles/ExtraParticles')
0154 part.enableUI()
0155 seq.adopt(part)
0156 part.pdgfile = self.pdgfile
0157
0158
0159 if self.rangecut is not None:
0160 seq = kernel.physicsList()
0161 rg = PhysicsList(kernel, 'Geant4DefaultRangeCut/GlobalRangeCut')
0162 rg.enableUI()
0163 seq.adopt(rg)
0164 rg.RangeCut = self.rangecut
0165
0166 for func in self._userFunctions:
0167 try:
0168 func(kernel)
0169 except Exception as e:
0170 logger.error("Exception in UserFunction: %r", e)
0171 raise RuntimeError("Exception in UserFunction: %r" % e)
0172
0173 return seq
0174
0175 def setupUserPhysics(self, userFunction):
0176 """To load arbitrary plugins, add a function to be executed.
0177
0178 The function must take the DDG4.Kernel() object as the only argument.
0179
0180 For example, add a function definition and the call to a steering file::
0181
0182 def setupCerenkov(kernel):
0183 from DDG4 import PhysicsList
0184 seq = kernel.physicsList()
0185 cerenkov = PhysicsList(kernel, 'Geant4CerenkovPhysics/CerenkovPhys')
0186 cerenkov.MaxNumPhotonsPerStep = 10
0187 cerenkov.MaxBetaChangePerStep = 10.0
0188 cerenkov.TrackSecondariesFirst = True
0189 cerenkov.VerboseLevel = 2
0190 cerenkov.enableUI()
0191 seq.adopt(cerenkov)
0192 ph = PhysicsList(kernel, 'Geant4OpticalPhotonPhysics/OpticalGammaPhys')
0193 ph.addParticleConstructor('G4OpticalPhoton')
0194 ph.VerboseLevel = 2
0195 ph.enableUI()
0196 seq.adopt(ph)
0197 return None
0198
0199 SIM.physics.setupUserPhysics(setupCerenkov)
0200
0201 # End of example
0202 """
0203 self._userFunctions.append(userFunction)