File indexing completed on 2026-07-04 07:50:42
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 EXCEPTION_SEVERITY_DICT = {
0012 "0": 0,
0013 "1": 1,
0014 "2": 2,
0015 "3": 3,
0016 "4": 4,
0017 "5": 5,
0018 "FatalException": 0,
0019 "FatalErrorInArgument": 1,
0020 "RunMustBeAborted": 2,
0021 "EventMustBeAborted": 3,
0022 "JustWarning": 4,
0023 "IgnoreTheIssue": 5,
0024 }
0025
0026
0027 class Physics(ConfigHelper):
0028 """Configuration for the PhysicsList and Monte Carlo particle selection."""
0029
0030 def __init__(self):
0031 super(Physics, self).__init__()
0032 self._ETolerance = None
0033 self._ESeverity = None
0034 self._ESeverity_EXTRA = {'choices': list(EXCEPTION_SEVERITY_DICT.keys())}
0035 self._rangecut = 0.7 * mm
0036 self._list = "FTFP_BERT"
0037 self._decays = False
0038 self._pdgfile = None
0039 self._rejectPDGs = {1, 2, 3, 4, 5, 6,
0040 21, 23, 24, 25,
0041 1103,
0042 2101, 2103, 2203,
0043 3101, 3103, 3201, 3203, 3303,
0044 4101, 4103, 4201, 4203, 4301, 4303, 4403,
0045 5101, 5103, 5201, 5203, 5301, 5303, 5401, 5403, 5503}
0046 self._zeroTimePDGs = {11, 13, 15, 17}
0047 self._decayByGeant = set()
0048 self._alternativeDecayStatuses = set()
0049 self._alternativeStableStatuses = set()
0050 self._userFunctions = []
0051 self._closeProperties()
0052 Physics.__doc__ += "\n\n" + self.setupUserPhysics.__doc__
0053
0054 @property
0055 def rejectPDGs(self):
0056 """Set of PDG IDs that will not be passed from the input record to Geant4.
0057
0058 Quarks, gluons and W's Z's etc should not be treated by Geant4
0059 """
0060 return self._rejectPDGs
0061
0062 @rejectPDGs.setter
0063 def rejectPDGs(self, val):
0064 self._rejectPDGs = self.makeSet(val)
0065
0066 @property
0067 def zeroTimePDGs(self):
0068 """Set of PDG IDs for particles that should not be passed to Geant4 if their properTime is 0.
0069
0070 The properTime of 0 indicates a documentation to add FSR to a lepton for example.
0071 """
0072 return self._zeroTimePDGs
0073
0074 @zeroTimePDGs.setter
0075 def zeroTimePDGs(self, val):
0076 self._zeroTimePDGs = self.makeSet(val)
0077
0078 @property
0079 def decayByGeant(self):
0080 """Set of PDG IDs for particles that should be decayed according to the lifetime.
0081
0082 Lifetimes are either configured in Geant4 or particle.tbl instead of their pre-assigned decay time.
0083
0084 """
0085 return self._decayByGeant
0086
0087 @decayByGeant.setter
0088 def decayByGeant(self, val):
0089 self._decayByGeant = self.makeSet(val)
0090
0091 @property
0092 def alternativeDecayStatuses(self):
0093 """Set of Generator Statuses that are used to mark unstable particles that should decay inside of Geant4.
0094 """
0095 return self._alternativeDecayStatuses
0096
0097 @alternativeDecayStatuses.setter
0098 def alternativeDecayStatuses(self, val):
0099 self._alternativeDecayStatuses = self.makeSet(val)
0100
0101 @property
0102 def alternativeStableStatuses(self):
0103 """Set of Generator Statuses that are used to mark stable particles that should be forwarded to Geant4.
0104 """
0105 return self._alternativeStableStatuses
0106
0107 @alternativeStableStatuses.setter
0108 def alternativeStableStatuses(self, val):
0109 self._alternativeStableStatuses = self.makeSet(val)
0110
0111 @property
0112 def rangecut(self):
0113 """ The global geant4 rangecut for secondary production
0114
0115 Default is 0.7 mm as is the case in geant4 10
0116
0117 To disable this plugin and be absolutely sure to use the Geant4 default range cut use "None"
0118
0119 Set printlevel to DEBUG to see a printout of all range cuts,
0120 but this only works if range cut is not "None"
0121 """
0122 return self._rangecut
0123
0124 @rangecut.setter
0125 def rangecut(self, val):
0126 if val is None:
0127 self._rangecut = None
0128 return
0129 if isinstance(val, str):
0130 if val == "None":
0131 self._rangecut = None
0132 return
0133 self._rangecut = val
0134
0135 @property
0136 def pdgfile(self):
0137 """Location of particle.tbl file containing extra particles and their lifetime information
0138
0139 For example in $DD4HEP/examples/DDG4/examples/particle.tbl
0140
0141 This is a vital setting if you want to simulate secondary vertices or pre-assigned decays or both. Geant4 has to
0142 know about all particles with non-negligible lifetime. Use this setting together with alternativeStableStatuses and
0143 alternativeDecayStatuses, to configure the simulation to suit your MCGenerator file.
0144 """
0145 return self._pdgfile
0146
0147 @pdgfile.setter
0148 def pdgfile(self, val):
0149 if not val:
0150 self._pdgfile = None
0151 return
0152 if not os.path.exists(val):
0153 raise RuntimeError("PDGFile: %s not found" % os.path.abspath(val))
0154 self._pdgfile = os.path.abspath(val)
0155
0156 @property
0157 def decays(self):
0158 """If true, add decay processes for all particles.
0159
0160 Only enable when creating a physics list not based on an existing Geant4 list!
0161 """
0162 return self._decays
0163
0164 @decays.setter
0165 def decays(self, val):
0166 self._decays = val
0167
0168 @property
0169 def list(self):
0170 """The name of the Geant4 Physics list."""
0171 return self._list
0172
0173 @list.setter
0174 def list(self, val):
0175 self._list = val
0176
0177 @property
0178 def ETolerance(self):
0179 """Configure the tolerance for the mass check of dynamic Particles, a.k.a, SetKETolerance
0180 """
0181 return self._ETolerance
0182
0183 @ETolerance.setter
0184 def ETolerance(self, val):
0185 self._ETolerance = val
0186
0187 @property
0188 def ESeverity(self):
0189 """Configure the severity for the mass check of dynamic Particles, a.k.a, SetKETolerance
0190 """
0191 return self._ESeverity
0192
0193 @ESeverity.setter
0194 def ESeverity(self, val):
0195 if val is None:
0196 self._ESeverity = val
0197 return
0198 val = EXCEPTION_SEVERITY_DICT[str(val)]
0199 self._ESeverity = val
0200
0201 def setupPhysics(self, kernel, name=None):
0202 seq = kernel.physicsList()
0203 seq.extends = name if name is not None else self.list
0204 seq.decays = self.decays
0205 seq.enableUI()
0206 seq.dump()
0207
0208 from DDG4 import PhysicsList
0209
0210
0211 if self.pdgfile:
0212 seq = kernel.physicsList()
0213 part = PhysicsList(kernel, 'Geant4ExtraParticles/ExtraParticles')
0214 part.enableUI()
0215 seq.adopt(part)
0216 part.pdgfile = self.pdgfile
0217
0218
0219 if self.rangecut is not None:
0220 seq = kernel.physicsList()
0221 rg = PhysicsList(kernel, 'Geant4DefaultRangeCut/GlobalRangeCut')
0222 rg.enableUI()
0223 seq.adopt(rg)
0224 rg.RangeCut = self.rangecut
0225
0226
0227 if self._ETolerance is not None or self._ESeverity is not None:
0228 seq = kernel.physicsList()
0229 rg = PhysicsList(kernel, 'Geant4SetKETolerance/KETolerance')
0230 rg.enableUI()
0231 seq.adopt(rg)
0232 if self._ETolerance is not None:
0233 rg.Tolerance = self._ETolerance
0234 if self._ESeverity is not None:
0235 rg.Severity = self._ESeverity
0236
0237 for func in self._userFunctions:
0238 try:
0239 func(kernel)
0240 except Exception as e:
0241 logger.error("Exception in UserFunction: %r", e)
0242 raise RuntimeError("Exception in UserFunction: %r" % e)
0243
0244 return seq
0245
0246 def setupUserPhysics(self, userFunction):
0247 """To load arbitrary plugins, add a function to be executed.
0248
0249 The function must take the DDG4.Kernel() object as the only argument.
0250
0251 For example, add a function definition and the call to a steering file::
0252
0253 def setupCerenkov(kernel):
0254 from DDG4 import PhysicsList
0255 seq = kernel.physicsList()
0256 cerenkov = PhysicsList(kernel, 'Geant4CerenkovPhysics/CerenkovPhys')
0257 cerenkov.MaxNumPhotonsPerStep = 10
0258 cerenkov.MaxBetaChangePerStep = 10.0
0259 cerenkov.TrackSecondariesFirst = True
0260 cerenkov.VerboseLevel = 2
0261 cerenkov.enableUI()
0262 seq.adopt(cerenkov)
0263 ph = PhysicsList(kernel, 'Geant4OpticalPhotonPhysics/OpticalGammaPhys')
0264 ph.addParticleConstructor('G4OpticalPhoton')
0265 ph.VerboseLevel = 2
0266 ph.enableUI()
0267 seq.adopt(ph)
0268 return None
0269
0270 SIM.physics.setupUserPhysics(setupCerenkov)
0271
0272 # End of example
0273 """
0274 self._userFunctions.append(userFunction)