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