ATLAS Offline Software
Loading...
Searching...
No Matches
AFP_TDSensitiveDetector.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
5// Class header
7
8// Athena headers
11
12// Geant4 headers
13#include "G4Version.hh"
14#include "G4TouchableHistory.hh"
15#include "G4Step.hh"
16#include "G4Track.hh"
17#include "G4ParticleDefinition.hh"
18#include "G4StepPoint.hh"
19#include "G4ThreeVector.hh"
20#include "G4Poisson.hh"
21#include "G4VSolid.hh"
22#include "G4ReflectedSolid.hh"
23#include "G4Material.hh"
24#include "G4MaterialPropertyVector.hh"
25
26// STL header
27#include <sstream>
28
29
30//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
31
32AFP_TDSensitiveDetector::AFP_TDSensitiveDetector(const std::string& name, const std::string& hitCollectionName)
33 : G4VSensitiveDetector( name )
34 , m_nHitID(-1)
35 , m_hitCollectionName(hitCollectionName)
36{
37}
38
39//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
40// Initialize from G4.
42{
44}
45
46//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
47
48bool AFP_TDSensitiveDetector::ProcessHits(G4Step* pStep, G4TouchableHistory*)
49{
50 if (!m_HitColl) {
52 if (!m_HitColl) {
53 return false;
54 }
55 }
56
57 if(verboseLevel>5)
58 {
59 G4cout << "AFP_TDSensitiveDetector::ProcessHits" << G4endl;
60 }
61
62 bool bRes=false;
63
64 int nTrackID=-1;
65 int nParticleEncoding=-1;
66 float fKineticEnergy=0.0;
67 float fEnergyDeposit=0.0;
68 float fWaveLength=0.0;
69 float fPreStepX=0.0;
70 float fPreStepY=0.0;
71 float fPreStepZ=0.0;
72 float fPostStepX=0.0;
73 float fPostStepY=0.0;
74 float fPostStepZ=0.0;
75 float fGlobalTime=0.0;
76 int nStationID=-1;
77 int nDetectorID=-1;
78 int nQuarticID=-1;
79 // int nPixelRow=-1;
80 // int nPixelCol=-1;
81
82 // step, track and particle info
83 G4Track* pTrack = pStep->GetTrack();
84 G4ParticleDefinition* pParticleDefinition = pTrack->GetDefinition();
85 G4StepPoint* pPreStepPoint = pStep->GetPreStepPoint();
86 G4StepPoint* pPostStepPoint = pStep->GetPostStepPoint();
87 G4ThreeVector PreStepPointPos = pPreStepPoint->GetPosition();
88 G4ThreeVector PostStepPointPos = pPostStepPoint->GetPosition();
89
90 nTrackID=pTrack->GetTrackID();
91 fKineticEnergy = pPreStepPoint->GetKineticEnergy();
92 fEnergyDeposit = pStep->GetTotalEnergyDeposit();
93
94 fPreStepX = PreStepPointPos.x();
95 fPreStepY = PreStepPointPos.y();
96 fPreStepZ = PreStepPointPos.z();
97 fPostStepX = PostStepPointPos.x();
98 fPostStepY = PostStepPointPos.y();
99 fPostStepZ = PostStepPointPos.z();
100 nParticleEncoding = pParticleDefinition->GetPDGEncoding();
101 fGlobalTime = pStep->GetPreStepPoint()->GetGlobalTime()/CLHEP::picosecond; // time w.r.t. prestep or poststep ??
102
103 // name of physical volume
104 G4TouchableHandle touch1 = pPreStepPoint->GetTouchableHandle();
105 G4VPhysicalVolume* volume = touch1->GetVolume();
106 G4String VolumeName = volume->GetName();
107
108 //G4ThreeVector ph0 = pStep->GetDeltaPosition().unit();
109 //if (fKineticEnergy<10000. && ph0.y()>.2) pTrack->SetTrackStatus(fKillTrackAndSecondaries);
110 //if (VolumeName.contains("TDQuarticBar")) return 1;
111
112 if(verboseLevel>5)
113 {
114 G4cout << "hit volume name is " << VolumeName << G4endl;
115
116 G4cout << "global, x_pre: " << fPreStepX << ", y_pre: " << fPreStepY << ", z_pre: " << fPreStepZ << G4endl;
117 G4cout << "global, x_post: " << fPostStepX << ", y_post: " << fPostStepY << ", z_post: " << fPostStepZ << G4endl;
118 }
119 //scan station and detector id
120 char* ppv1, *ppv2;
121 char szbuff[32];
122 memset(&szbuff[0],0,sizeof(szbuff));
123 strncpy(szbuff,VolumeName.data(),sizeof(szbuff));
124 szbuff[sizeof(szbuff)-1] = '\0'; // idiomatic use of strncpy...
125 ppv1=strchr(szbuff,'[');
126 ppv2=strchr(szbuff,']');
127 if(!ppv2 || !ppv1){
128 G4cout << "ERROR: Invalid format of volume name " << VolumeName << G4endl;
129 return false;
130 }
131 else *ppv2='\0';
132
133 nStationID=10*(szbuff[3]-0x30)+(szbuff[4]-0x30);
134 nDetectorID=atoi(ppv1+1);
135
136 m_nHitID++;
137
138 /*
139 if (VolumeName.contains("TDSensor") || (bRes=VolumeName.contains("TDQuarticBar["))){
140 nQuarticID=szbuff[7]-0x30;
141
142 if(VolumeName.contains("TDSensor") && pStep->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName()!="OpAbsorption" )
143 {
144 //hit in TD sensor but with no OpAbsorption (transportation)
145 }
146 else{
147
148 fWaveLength = 2.*M_PI*CLHEP::hbarc/(CLHEP::MeV*CLHEP::nm)/fKineticEnergy;
149
150 if (fWaveLength > 800. || fWaveLength < 200.) return 1; // 200-800 nm cut
151 AFP_TDSimHit* pHit = new AFP_TDSimHit();
152 pHit->m_nHitID=m_nHitID;
153 pHit->m_nTrackID=nTrackID;
154 pHit->m_nParticleEncoding=nParticleEncoding;
155 pHit->m_fKineticEnergy=fKineticEnergy;
156 pHit->m_fEnergyDeposit=fEnergyDeposit;
157 pHit->m_fWaveLength=fWaveLength;
158 pHit->m_fPreStepX=fPreStepX;
159 pHit->m_fPreStepY=fPreStepY;
160 pHit->m_fPreStepZ=fPreStepZ;
161 pHit->m_fPostStepX=fPostStepX;
162 pHit->m_fPostStepY=fPostStepY;
163 pHit->m_fPostStepZ=fPostStepZ;
164 pHit->m_fGlobalTime=fGlobalTime;
165
166 pHit->m_nStationID=nStationID;
167 pHit->m_nDetectorID=nDetectorID;
168 pHit->m_nSensitiveElementID=(bRes? 2:1)+2*nQuarticID;//Q1: 1-2, Q2: 3-4
169 m_HitColl->Insert(*pHit);
170 }
171 }
172 */
173
174#if G4VERSION_NUMBER < 1100
175 if ( (VolumeName.contains("TDQuarticBarVacBorder")) && pParticleDefinition->GetPDGCharge() !=0 )
176#else
177 if ( (G4StrUtil::contains(VolumeName, "TDQuarticBarVacBorder")) && pParticleDefinition->GetPDGCharge() !=0 )
178#endif
179 {
180 nQuarticID=szbuff[7]-0x30;
181 /*
182 m_HitColl->Emplace(m_nHitID,nTrackID,nParticleEncoding,fKineticEnergy,fEnergyDeposit,
183 fWaveLength,fPreStepX,fPreStepY,fPreStepZ,fPostStepX,fPostStepY,
184 fPostStepZ,fGlobalTime,nStationID,nDetectorID,(2+2*nQuarticID));//Q1: 1-2, Q2: 3-4
185 // m_HitColl->Emplace(m_nHitID,nTrackID,nParticleEncoding,fKineticEnergy,fEnergyDeposit,
186 // fWaveLength,fPreStepX,fPreStepY,fPreStepZ,fPostStepX,fPostStepY,
187 // fPostStepZ,fGlobalTime,nStationID,nDetectorID,((bRes? 2:1)+2*nQuarticID));//Q1: 1-2, Q2: 3-4
188 */
189 }
190
192#if G4VERSION_NUMBER < 1100
193 if ( (bRes=VolumeName.contains("TDQuarticBar[")) )
194#else
195 if ( (bRes=G4StrUtil::contains(VolumeName, "TDQuarticBar[")) )
196#endif
197 {
198 nQuarticID=szbuff[7]-0x30;
199
200 // Cut on maximum number of generated photons/bar
201 if (m_HitColl->HasReachedLimit(nStationID, nQuarticID, nDetectorID)) return 1;
202
203 // Get the Touchable History:
204 const G4TouchableHistory* myTouch = static_cast<const G4TouchableHistory*>(pPreStepPoint->GetTouchable());
205 // Calculate the local step position.
206 // From a G4 FAQ:
207 // http://geant4-hn.slac.stanford.edu:5090/HyperNews/public/get/geometry/17/1.html
208 const G4AffineTransform transformation = myTouch->GetHistory()->GetTopTransform();
209 G4ThreeVector PreStepPointPos2 = transformation.TransformPoint(PreStepPointPos);
210 G4ThreeVector PostStepPointPos2 = transformation.TransformPoint(PostStepPointPos);
211
212 G4String shape( myTouch->GetSolid()->GetEntityType() );
213
214 G4ThreeVector normpX( 1., 0., 0.);
215 G4ThreeVector normnX(-1., 0., 0.);
216 G4ThreeVector normpY( 0., 1., 0.);
217 G4ThreeVector normnY( 0.,-1., 0.);
218 G4ThreeVector normpZ( 0., 0., 1.);
219 G4ThreeVector normnZ( 0., 0.,-1.);
220
221 //G4double BarpX = ((G4ReflectedSolid *)(myTouch->GetSolid()))->DistanceToOut(PreStepPointPos2, normpX);
222 //G4double BarnX = ((G4ReflectedSolid *)(myTouch->GetSolid()))->DistanceToOut(PreStepPointPos2, normnX);
223 //G4double BarpY = ((G4ReflectedSolid *)(myTouch->GetSolid()))->DistanceToOut(PreStepPointPos2, normpY);
224 //G4double BarnY = ((G4ReflectedSolid *)(myTouch->GetSolid()))->DistanceToOut(PreStepPointPos2, normnY);
225 //G4double BarpZ = ((G4ReflectedSolid *)(myTouch->GetSolid()))->DistanceToOut(PreStepPointPos2, normpZ);
226 //G4double BarnZ = ((G4ReflectedSolid *)(myTouch->GetSolid()))->DistanceToOut(PreStepPointPos2, normnZ);
227
228 //G4double BarHalfX = .5 * (BarpX+BarnX);
229 //G4double BarHalfY = .5 * (BarpY+BarnY);
230 //G4double BarHalfZ = .5 * (BarpZ+BarnZ);
231
232 G4double PreProtonX = PreStepPointPos2.x();
233 G4double PreProtonY = PreStepPointPos2.y();
234 G4double PreProtonZ = PreStepPointPos2.z();
235
236 G4double PostProtonX = PostStepPointPos2.x();
237 G4double PostProtonY = PostStepPointPos2.y();
238 G4double PostProtonZ = PostStepPointPos2.z();
239
240 G4ThreeVector p0 = pStep->GetDeltaPosition().unit();
241
242 G4Material* mat = pStep->GetTrack()->GetMaterial();
243 G4MaterialPropertiesTable *matPropTable = mat->GetMaterialPropertiesTable();
244 // Refractive index
245 G4MaterialPropertyVector* Rind = matPropTable->GetProperty("RINDEX");
246 // Absorbtion length
247 G4MaterialPropertyVector* Alen = matPropTable->GetProperty("ABSLENGTH");
248
249 const G4double charge = pParticleDefinition->GetPDGCharge();
250 const G4double beta = (pPreStepPoint->GetBeta() + pPostStepPoint->GetBeta()) / 2.;
251 G4double BetaInverse = 1. / beta;
252
253 //G4int Rsize = Rind->Entries() - 1;
254 //G4double Pmin = Rind->GetMinPhotonEnergy(); // 800 nm
255#if G4VERSION_NUMBER < 1100
256 G4double Pmin = Rind->GetMinLowEdgeEnergy();
257 //G4double Pmax = Rind->GetMaxPhotonEnergy(); // 200 nm
258 G4double Pmax = Rind->GetMaxLowEdgeEnergy();
259#else
260 G4double Pmin = Rind->GetMinEnergy();
261 G4double Pmax = Rind->GetMaxEnergy();
262#endif
263 G4double dp = Pmax - Pmin;
264 //G4double maxCosTheta = BetaInverse / Rind->GetMinProperty();
265 G4double maxCosTheta = BetaInverse / Rind->GetMinValue();
266 G4double maxSin2Theta = (1.0 - maxCosTheta) * (1.0 + maxCosTheta);
267
268 //G4double meanRI = .5*(Rind->GetMinProperty() + Rind->GetMaxProperty());
269 G4double meanRI = .5*(Rind->GetMinValue() + Rind->GetMaxValue());
270
271 // Formula taken from G4 docu (to be changed since the integral approximation)
272 G4double MeanNumberOfPhotons = 370.*(charge/CLHEP::eplus)*(charge/CLHEP::eplus)* (1.0 - 1.0/(beta * meanRI * beta * meanRI)) / (CLHEP::cm*CLHEP::eV);
273 if (MeanNumberOfPhotons <= 0.0) return 1;
274
275 G4double step_length = pStep->GetStepLength();
276
277 MeanNumberOfPhotons = MeanNumberOfPhotons * step_length * dp;
278 G4int NumPhotons = (G4int) G4Poisson( MeanNumberOfPhotons );
279 if (NumPhotons <= 0) return 1;
280 //ATH_MSG_INFO("number of photons: " << NumPhotons);
281
282 G4int NumPhotonsCuts=0;
283 for (G4int I = 0; I < NumPhotons; I++) {
284
285 G4double rand;
286 G4double sampledEnergy, sampledRI;
287 G4double cosTheta, sin2Theta;
288
289 // Sample an energy for photon (using MC elimination method)
290 do {
291 rand = G4UniformRand();
292 sampledEnergy = Pmin + rand * dp;
293 //sampledRI = Rind->GetProperty(sampledEnergy);
294 sampledRI = Rind->Value(sampledEnergy);
295 cosTheta = BetaInverse / sampledRI;
296
297 sin2Theta = (1.0 - cosTheta)*(1.0 + cosTheta);
298 rand = G4UniformRand();
299
300 } while (rand * maxSin2Theta > sin2Theta);
301
302 // Generate random position of photon on the cone surface defined by Theta
303 rand = G4UniformRand();
304 G4double phi = 2.*M_PI*rand;
305 G4double sinPhi = sin(phi);
306 G4double cosPhi = cos(phi);
307
308 // Calculate x,y,z coordinates of photon momentum
309 // in coordinate system with primary particle direction aligned with the z-axis
310 // + Rotate momentum direction back to the global coordinate system
311 G4double sinTheta = sqrt(sin2Theta);
312 G4double px = sinTheta*cosPhi;
313 G4double py = sinTheta*sinPhi;
314 G4double pz = cosTheta;
315 G4ParticleMomentum photonMomentum(px, py, pz);
316 photonMomentum.rotateUz(p0);
317
318 G4double PX = photonMomentum.getX();
319 G4double PY = photonMomentum.getY();
320 G4double PZ = photonMomentum.getZ();
321
322 // calculate projections coordinates
323 //G4double PXp = PX/sqrt(PX*PX+PY*PY+PZ*PZ);
324 G4double PYp = PY/sqrt(PX*PX+PY*PY+PZ*PZ);
325 G4double PZp = PZ/sqrt(PX*PX+PY*PY+PZ*PZ);
326
327 G4double PYt = PY/sqrt(PY*PY+PZ*PZ);
328 G4double PZt = PZ/sqrt(PY*PY+PZ*PZ);
329
330 // Cosines (alpha, delta)
331 cosPhi = (PYp*PYt + PZp*PZt);
332 if (nStationID == 0) cosTheta = ( -PYt*sin(48.*CLHEP::deg) + PZt*cos(48.*CLHEP::deg) );
333 else cosTheta = ( -PYt*sin(48.*CLHEP::deg) - PZt*cos(48.*CLHEP::deg) );
334
335 // Total internal reflection conditions
336 G4double cosThetaC = sqrt(1.-1./sampledRI/sampledRI);
337 if (sqrt(1.-cosPhi*cosPhi)>cosThetaC) continue;
338 if (sqrt(1.-cosTheta*cosTheta)*cosPhi>cosThetaC) continue;
339
340 // Parametric equation of line where photons are generated
341 rand = G4UniformRand();
342 G4double PhotonX = PreProtonX + (PostProtonX-PreProtonX)*rand;
343 G4double PhotonY = PreProtonY + (PostProtonY-PreProtonY)*rand;
344 G4double PhotonZ = PreProtonZ + (PostProtonZ-PreProtonZ)*rand;
345 G4ThreeVector PhotonPos(PhotonX,PhotonY,PhotonZ);
346 G4double PhotonR = sqrt( (PreProtonX-PhotonX)*(PreProtonX-PhotonX) + (PreProtonY-PhotonY)*(PreProtonY-PhotonY) + (PreProtonZ-PhotonZ)*(PreProtonZ-PhotonZ) );
347
348 G4double Y0;
349 // including the scattering from the top edge of the bar (perfect absorber now)
350 if (cosTheta>=0)
351 {
352 Y0 = static_cast<G4ReflectedSolid *>(myTouch->GetSolid())->DistanceToOut(PhotonPos, normnY);
353 Y0 = Y0/cosTheta/cosPhi;
354 }
355 else
356 {
357 continue;
358 //Y0 = 2.*((G4ReflectedSolid *)(myTouch->GetSolid()))->DistanceToOut(PhotonPos, normpY) + ((G4ReflectedSolid *)(myTouch->GetSolid()))->DistanceToOut(PhotonPos, normnY);
359 //Y0 = -Y0/cosTheta/cosPhi;
360 }
361
362 // absorption of photons inside the crystal
363 //float Pabs = 1. - exp( - Y0/Alen->GetProperty(sampledEnergy) );
364 float Pabs = 1. - exp( - Y0/Alen->Value(sampledEnergy) );
365 rand = G4UniformRand();
366 if (Pabs>rand) continue;
367
368 // maximum PMT efficiency cut (15%) to to avoid crashes due to the too large memory consumption
369 rand = G4UniformRand();
370 if (rand>TDMaxQEff) continue;
372
373 NumPhotonsCuts++;
374
375 float fGlobalTime2 = fGlobalTime;
376 fGlobalTime2 += ( PhotonR * BetaInverse / CLHEP::c_light )/CLHEP::picosecond;
377
378 // for group velocity of light: Edn/dE
379 float EdndE;
380 //if (sampledEnergy > (Pmin+.5*dp)) EdndE = (sampledRI - Rind->GetProperty(sampledEnergy-0.0001*CLHEP::eV))/0.0001*sampledEnergy/CLHEP::eV;
381 if (sampledEnergy > (Pmin+.5*dp)) EdndE = (sampledRI - Rind->Value(sampledEnergy-0.0001*CLHEP::eV))/0.0001*sampledEnergy/CLHEP::eV;
382 //else EdndE = (Rind->GetProperty(sampledEnergy+0.0001*CLHEP::eV) - sampledRI)/0.0001*sampledEnergy/CLHEP::eV;
383 else EdndE = (Rind->Value(sampledEnergy+0.0001*CLHEP::eV) - sampledRI)/0.0001*sampledEnergy/CLHEP::eV;
384 fGlobalTime2 += ( (sampledRI + EdndE)* Y0 * BetaInverse / CLHEP::c_light )/CLHEP::picosecond;
385
386 if (verboseLevel>5)
387 {
388 G4cout << "FastCher EdndE: " << EdndE << G4endl;
389 }
390 fWaveLength = 2.*M_PI*CLHEP::hbarc/sampledEnergy/(CLHEP::MeV*CLHEP::nm);
391
392 // Cut on maximum number of generated photons/bar
393 if (m_HitColl->HasReachedLimit(nStationID, nQuarticID, nDetectorID)) return 1;
394
395 int nSensitiveElementID=-1;
396 if(nQuarticID==0) { nSensitiveElementID=1; }
397 else if(nQuarticID==1) { nSensitiveElementID=3; }
398
399 m_HitColl->Emplace(m_nHitID,nTrackID,nParticleEncoding,fKineticEnergy,fEnergyDeposit,
400 fWaveLength,PhotonX,PhotonY,PhotonZ,(PhotonX+PX),(PhotonY+PY),(PhotonZ+PZ),
401 fGlobalTime2,nStationID,nDetectorID,nSensitiveElementID);
402 m_HitColl->CountHit(nStationID, nQuarticID, nDetectorID);
403 }
404 if(verboseLevel>5)
405 {
406 G4cout << "FastCher number of photons: " << NumPhotonsCuts << G4endl;
407 }
408 }
410 return true;
411}
412
414{
415 auto* eventInfo = AtlasG4EventUserInfo::GetEventUserInfo();
416 if (!eventInfo) {
417 return nullptr;
418 }
419 auto hitCollections = eventInfo->GetHitCollectionMap();
420 return hitCollections ? hitCollections->Find<AFP_TDSimHitCollectionBuilder>(m_hitCollectionName) : nullptr;
421}
#define M_PI
Scalar phi() const
phi method
double charge(const T &p)
Definition AtlasPID.h:997
#define I(x, y, z)
Definition MD5.cxx:116
AFP_TDSimHitCollectionBuilder * m_HitColl
AFP_TDSimHitCollectionBuilder * getHitCollection() const
G4bool ProcessHits(G4Step *, G4TouchableHistory *) override final
static constexpr double TDMaxQEff
AFP_TDSensitiveDetector(const std::string &name, const std::string &hitCollectionName)
void Initialize(G4HCofThisEvent *) override final
static AtlasG4EventUserInfo * GetEventUserInfo()