EDepSimDokeBirks.cc
Go to the documentation of this file.
1 #include <G4ParticleTypes.hh> //lets you refer to G4OpticalPhoton, etc.
2 #include <G4EmProcessSubType.hh> //lets you call this process Scintillation
3 #include <G4Version.hh> //tells you what Geant4 version you are running
4 #include <G4SystemOfUnits.hh>
5 #include <G4PhysicalConstants.hh>
6 #include <G4FieldManager.hh>
7 #include <G4Field.hh>
8 
9 
10 #include "EDepSimLog.hh"
11 
12 #include "EDepSimDokeBirks.hh"
13 
14 // The physics in EDepSim::DokeBirks is shamelessly stolen from NEST, so cite
15 // the NEST paper to give credit where credit is due. However, all of the
16 // bugs are mine, so there should be a note like "Simplified implementation of
17 // the physics described in NEST paper".
18 EDepSim::DokeBirks::DokeBirks(const G4String& processName,
19  G4ProcessType type)
20  : G4VRestDiscreteProcess(processName, type),
21  fEmSaturation(nullptr)
22 {
23  SetProcessSubType(fScintillation);
24 
25  if (verboseLevel>0) {
26  G4cout << GetProcessName() << " is created " << G4endl;
27  }
28 
29 }
30 
31 EDepSim::DokeBirks::~DokeBirks () {} //destructor needed to avoid linker error
32 
33 
34 G4bool
35 EDepSim::DokeBirks::IsApplicable(const G4ParticleDefinition& aParticleType)
36 {
37  // This process is applicable to some neutral particles (e.g. gammas)
38  // since G4 will fake creating the low energy electrons.
39  if (aParticleType.IsShortLived()) return false;
40  if (aParticleType.GetParticleName() == "opticalphoton") return false;
41  if (aParticleType.GetParticleName() == "thermalelectron") return false;
42  // Not applicable to the neutrinos.
43  if (std::abs(aParticleType.GetPDGEncoding())==12) return false;
44  if (std::abs(aParticleType.GetPDGEncoding())==14) return false;
45  if (std::abs(aParticleType.GetPDGEncoding())==16) return false;
46  return true;
47 }
48 
49 // This routine simply calls the equivalent PostStepDoIt since all the
50 // necessary information resides in aStep.GetTotalEnergyDeposit()
51 G4VParticleChange*
52 EDepSim::DokeBirks::AtRestDoIt(const G4Track& aTrack, const G4Step& aStep)
53 {
54  return EDepSim::DokeBirks::PostStepDoIt(aTrack, aStep);
55 }
56 
57 // this is the most important function, where all light & charge yields
58 // happen!
59 G4VParticleChange*
60 EDepSim::DokeBirks::PostStepDoIt(const G4Track& aTrack, const G4Step& aStep)
61 {
62  aParticleChange.Initialize(aTrack);
63 
64  const G4Material* aMaterial = aTrack.GetMaterial();
65 
66  bool inLiquidArgon = true;
67  // Check that we are in liquid argon.
68  if (aMaterial->GetState() != kStateLiquid) {
69  // Do Nothing
70  if (aMaterial->GetState() == kStateUndefined) {
71  EDepSimError("Undefined material state for "
72  << aMaterial->GetName());
73  }
74  inLiquidArgon = false;
75  }
76  if (aMaterial->GetNumberOfElements() > 1) {
77  // Do Nothing
78  inLiquidArgon = false;
79  }
80  if (std::abs(aMaterial->GetElement(0)->GetZ()-18.0) > 0.5) {
81  // Do Nothing
82  inLiquidArgon = false;
83  }
84 
85  const G4DynamicParticle* aParticle = aTrack.GetDynamicParticle();
86  const G4ParticleDefinition *pDef = aParticle->GetDefinition();
87  G4String particleName = pDef->GetParticleName();
88  // G4StepPoint* pPreStepPoint = aStep.GetPreStepPoint();
89  G4double totalEnergyDeposit = aStep.GetTotalEnergyDeposit();
90 
91  if (totalEnergyDeposit <= 0.0) {
92  return G4VRestDiscreteProcess::PostStepDoIt(aTrack, aStep);
93  }
94 
95  if (aStep.GetStepLength() <= 0.0) {
96  return G4VRestDiscreteProcess::PostStepDoIt(aTrack, aStep);
97  }
98 
99  if (!inLiquidArgon) {
100  if (!fEmSaturation) fEmSaturation = new G4EmSaturation(0);
101  G4double nonIonizingEnergy
102  = fEmSaturation->VisibleEnergyDepositionAtAStep(&aStep);
103  aParticleChange.ProposeNonIonizingEnergyDeposit(nonIonizingEnergy);
104  return G4VRestDiscreteProcess::PostStepDoIt(aTrack, aStep);
105  }
106 
107  // Figure out the electric field for the volume.
108  G4StepPoint* pPostStepPoint = aStep.GetPostStepPoint();
109  const G4VPhysicalVolume* aVolume = pPostStepPoint->GetPhysicalVolume();
110  const G4LogicalVolume* aLogVolume = aVolume->GetLogicalVolume();
111  const G4FieldManager* aFieldManager = aLogVolume->GetFieldManager();
112  if (!aFieldManager) {
113  return G4VRestDiscreteProcess::PostStepDoIt(aTrack, aStep);
114  }
115  if (!aFieldManager->DoesFieldExist()) {
116  return G4VRestDiscreteProcess::PostStepDoIt(aTrack, aStep);
117  }
118  const G4Field* aField = aFieldManager->GetDetectorField();
119  if (!aField) {
120  EDepSimError("LAr Volume "
121  << aLogVolume->GetName()
122  << " should have field!");
123  return G4VRestDiscreteProcess::PostStepDoIt(aTrack, aStep);
124  }
125 
126  G4ThreeVector thePosition = pPostStepPoint->GetPosition();
127  double theTime = pPostStepPoint->GetGlobalTime();
128  double point[4] = {
129  thePosition.x(), thePosition.y(), thePosition.z(), theTime};
130  double bField[6];
131  aField->GetFieldValue(point,bField);
132 
133  double electricField = bField[3]*bField[3];
134  electricField += bField[4]*bField[4];
135  electricField += bField[5]*bField[5];
136  if (electricField > 0) {
137  electricField = std::sqrt(electricField);
138  }
139  // The code below is pulled from G4S1Light and is simplified to be
140  // Doke-Birks only, in LAr only, and for an electric field only. This is
141  // for ARGON only. The Doke-Birks constants are in kilovolt/cm
142  G4double dokeBirks[3];
143 
144  dokeBirks[0] = 0.07*pow((electricField),-0.85);
145  dokeBirks[2] = 0.00;
146  dokeBirks[1] = dokeBirks[0]/(1-dokeBirks[2]); //B=A/(1-C) (see paper)
147 
148  G4double dE = totalEnergyDeposit/MeV;
149  G4double dx = aStep.GetStepLength()/cm;
150  G4double density = aMaterial->GetDensity()/(g/cm3);
151  G4double LET = 0.0; //lin. energy transfer (prop. to dE/dx)
152  if (dx != 0.0) LET = (dE/dx)*(1/density);
153 
154  // There was some dEdX for a photon. This means that the very low energy
155  // electrons were not simulated.
156  if (particleName == "gamma") {
157  LET = CalculateElectronLET( 1000*dE );
158  dx = dE/density/LET;
159  }
160  else if (std::abs(pDef->GetPDGCharge()) < 0.1) {
161  // Handle the case where a neutral particle made an "electron". This
162  // is when there is a tiny electron scatter that is below the G4
163  // threshold. The assumption is the same as NEST. The product
164  // particle is an electron.
165  double tempLET = CalculateElectronLET( 1000*dE );
166  double electronStep = dE/density/tempLET;
167  if (dx > electronStep) {
168  LET = tempLET;
169  dx = electronStep;
170  }
171  }
172 
173  // Special case for electrons that G4 may stop when they get to short and
174  // truncate the energy deposition.
175  do {
176  if (particleName != "e+" || particleName != "e-") break;
177  if (aTrack.GetCurrentStepNumber() != 1) break;
178  double ratio = CalculateElectronLET( 1000.0*dE ) / LET;
179  if (ratio > 0.7) break;
180  LET *= ratio;
181  dx /= ratio;
182  } while (false);
183 
184  G4double recombProb = (dokeBirks[0]*LET)/(1+dokeBirks[1]*LET)+dokeBirks[2];
185 
186  G4double ScintillationYield = 1 / (19.5*eV);
187  G4double NumQuanta = ScintillationYield*totalEnergyDeposit;
188  G4double ExcitationRatio = 0.21; //Aprile et. al book
189  G4double NumExcitons = NumQuanta*ExcitationRatio/(1+ExcitationRatio);
190  G4double NumIons = NumQuanta - NumExcitons;
191  G4double NumPhotons = NumExcitons + NumIons*recombProb;
192 
193  // The number of photons and electrons for this step has been calculated
194  // using the Doke/Birks law version of recombination. For DETSIM we are
195  // only looking at argon, and according the Matt both Doke-Birks and
196  // Thomas-Imel fit the data. It's also because we are not interested in
197  // extremely low energies, so Thomas-Imel makes a negligible correction.
198  G4double nonIonizingEnergy = totalEnergyDeposit;
199  nonIonizingEnergy *= NumPhotons/NumQuanta;
200  aParticleChange.ProposeNonIonizingEnergyDeposit(nonIonizingEnergy);
201  return G4VRestDiscreteProcess::PostStepDoIt(aTrack, aStep);
202 }
203 
204 // GetMeanFreePath
205 // ---------------
206 G4double EDepSim::DokeBirks::GetMeanFreePath(const G4Track&,
207  G4double ,
208  G4ForceCondition* condition)
209 {
210  *condition = StronglyForced;
211  // Force the EDepSim::DokeBirks physics process to always happen, even if it's
212  // not the dominant process. In effect it is a meta-process on top of any
213  // and all other energy depositions which may occur, just like the
214  // original G4Scintillation (disregard DBL_MAX, this function makes the
215  // mean free path zero really, not infinite)
216 
217  return DBL_MAX;
218 }
219 
220 // GetMeanLifeTime
221 // ---------------
222 G4double EDepSim::DokeBirks::GetMeanLifeTime(const G4Track&,
223  G4ForceCondition* condition)
224 {
225  *condition = Forced;
226  // This function and this condition has the same effect as the above
227  return DBL_MAX;
228 }
229 
230 // Get the expected electron electron dEdX as a function of energy. This is
231 // normalized to density. The energy must be in MeV for the parametrization
232 // to work. This is only applicable to argon.
234  G4double LET;
235  if ( E >= 1 ) LET = 116.70-162.97*log10(E)+99.361*pow(log10(E),2)-
236  33.405*pow(log10(E),3)+6.5069*pow(log10(E),4)-
237  0.69334*pow(log10(E),5)+.031563*pow(log10(E),6);
238  else if ( E>0 && E<1 ) LET = 100;
239  else LET = 0;
240  return LET;
241 }
242 
static constexpr double cm
Definition: Units.h:68
G4bool IsApplicable(const G4ParticleDefinition &aParticleType)
Determine which particles this process should be applied too.
static constexpr double g
Definition: Units.h:144
static constexpr double cm3
Definition: Units.h:70
constexpr T pow(T x)
Definition: pow.h:72
G4VParticleChange * PostStepDoIt(const G4Track &aTrack, const G4Step &aStep)
Apply the scintilation process for an in-flight particle.
static constexpr double MeV
Definition: Units.h:129
T abs(T value)
static constexpr double eV
Definition: Units.h:127
#define EDepSimError(outStream)
Definition: EDepSimLog.hh:503
G4double CalculateElectronLET(G4double E)
G4VParticleChange * AtRestDoIt(const G4Track &aTrack, const G4Step &aStep)
G4double GetMeanLifeTime(const G4Track &aTrack, G4ForceCondition *)
G4double GetMeanFreePath(const G4Track &aTrack, G4double, G4ForceCondition *)
DokeBirks(const G4String &processName="Doke-Birks", G4ProcessType type=fElectromagnetic)
G4EmSaturation * fEmSaturation