GEMC  2.3
Geant4 Monte-Carlo Framework
cnd_hitprocess.cc
Go to the documentation of this file.
1 // G4 headers
2 #include "G4Poisson.hh"
3 #include "Randomize.hh"
4 
5 // gemc headers
6 #include "cnd_hitprocess.h"
7 
8 // CLHEP units
9 #include "CLHEP/Units/PhysicalConstants.h"
10 using namespace CLHEP;
11 
12 map<string, double> cnd_HitProcess :: integrateDgt(MHit* aHit, int hitn)
13 {
14  string hd_msg = " > cnd hit process";
15 
16  map<string, double> dgtz;
17  vector<identifier> identity = aHit->GetId();
18  trueInfos tInfos(aHit);
19 
20  int layer = identity[0].id;
21  int paddle = identity[1].id;
22 
23  // Energy propagation:
24 
25  double Lg[3]; // lengths along light-guides to PMT (from design drawings)
26  Lg[0] = 139.56*cm; // inner layer
27  Lg[1] = 138.89*cm;
28  Lg[2] = 136.88*cm; // outer layer
29 
30  double att_length = 1.5*m; // light attenuation length in scintillator
31  double att_length_lg = 9.5*m; // light attenuation length in long light-guides
32 
33  double sensor_surface = pow(2.5*cm,2)*pi; // X-sectional area of PMT, assume radius of 2.5 cm.
34  double paddle_xsec = 0.; // cross-sectional area of the paddle
35  if (layer == 1) paddle_xsec = 22.8*cm*cm; // from the geometry files
36  else if (layer == 2) paddle_xsec = 26.4*cm*cm;
37  else if (layer == 3) paddle_xsec = 27.7*cm*cm;
38  double light_coll; // ratio of photo_sensor area over paddle section, times optical coupling ~ light collection efficiency
39  if (sensor_surface < paddle_xsec) light_coll = 0.7 * sensor_surface / paddle_xsec;
40  else light_coll = 0.7; // to make sure sensor_surface / paddle_xsec doesn't go over 1.
41 
42  double uturn[3]; // fraction of energy which makes it through the u-turn light-guides (based on cosmic tests)
43  uturn[0] = 0.65; // inner layer
44  uturn[1] = 0.6;
45  uturn[2] = 0.5; // outer layer
46 
47  double light_yield = 10000/MeV; // number of optical photons produced in the scintillator per MeV of deposited energy
48  double sensor_qe = 0.2; // photo sensor quantum efficiency
49  double sensor_gain = 0.24; // gain of the photo sensor in pC/(#p.e.); it defines the conversion from photoelectrons to charge:
50  // for a pmt gain of 1.5*10^6, this factor is equal to 1.5*10^6*1.6*10^-19 C = 0.24 pC/(#p.e.)
51  double signal_split = 0.5; // signal is split into two, going to QDC and discriminators.
52  double adc_conv = 10.; // conversion factor from pC to ADC (typical sensitivy of CAEN VME QDC is of 0.1 pC/ch)
53  double adc_ped = 3.; // ADC Pedestal
54 
55 
56  // Time of signal:
57 
58  double veff = 16*cm/ns; // light velocity in scintillator
59 
60  double t_u[3]; // time it takes for light to travel round u-turn lightguide for the three layers (based on cosmic tests)
61  t_u[0] = 0.6*ns; // inner layer
62  t_u[1] = 1.2*ns;
63  t_u[2] = 1.7*ns; // outer layer
64 
65  double sigmaTD = 0.14*ns/sqrt(MeV); // time smearing factor (estimated from tests at Orsay), same paddle as hit (in ns/sqrt(MeV)).
66  double sigmaTN = 0.14*ns/sqrt(MeV); // time smearing factor, neighbouring paddle to the hit one, units as above.
67 
68  double tdc_conv = 40/ns; // TDC conversion factor (1/0.025ns), channels per per ns.
69 
70 
71  // Get the paddle length: in CND paddles are along z
72  double length = aHit->GetDetector().dimensions[0]; // this is actually the half-length!
73 
74  // Get info about detector material to eveluate Birks effect
75  double birks_constant=aHit->GetDetector().GetLogical()->GetMaterial()->GetIonisation()->GetBirksConstant();
76 
77  vector<G4ThreeVector> Lpos = aHit->GetLPos();
78  vector<double> Edep = aHit->GetEdep();
79  // Charge for each step
80  vector<int> charge = aHit->GetCharges();
81  vector<double> times = aHit->GetTime();
82  vector<double> dx = aHit->GetDx();
83 
84  unsigned nsteps = times.size();
85 
86 
87  // Variables for each step:
88 
89  double dDir = 0.; // distance travelled by the light along the paddle UPSTREAM (direct light)
90  double dNeigh = 0.; // distance travelled by the light along the paddle DOWNSTREAM (and then through the neighbour paddle)
91  double Edep_B = 0.; // Energy deposit, scaled in accordance with Birk's effect
92 
93  double e_dir = 0.; // attenuated energy as it arrives at the two PMTs (one coupled to the hit paddle, one to its neighbour)
94  double e_neigh = 0.;
95 
96 
97  // Variables for each hit:
98 
99  // int flag_counted = 0; // Flag to specify that the time of hit has already been determined for this hit (1: yes, 0: no)
100 
101  double et_D = 0.; // variables to hold total energy*time values for each step, for the two PMTs
102  double et_N = 0.;
103 
104  double etotD = 0.; // total energy of hit propagated to the PMT connected to the hit paddle
105  double etotN = 0.; // total energy of hit propagated to downstream end of the hit paddle, round u-turn, along neighbouring paddle and light-guide and into PMT
106  double timeD = 0.; // hit times measured at the upstream edges of the two paddles
107  double timeN = 0.;
108 
109  int ADCD = 0;
110  int ADCN = 0;
111  int TDCD = 4096; // max value of the ADC readout
112  int TDCN = 4096;
113 
114 
115  if(tInfos.eTot>0)
116  {
117  for(unsigned int s=0; s<nsteps; s++)
118  {
119 
120  // Distances travelled through the paddles to the upstream edges (of the hit paddle and of its coupled neighbour):
121  dDir = length + Lpos[s].z();
122  dNeigh = 3*length - Lpos[s].z();
123 
124  // cout << "\n Distances: " << endl;
125  // cout << "\t dDir, dNeigh: " << dDir << ", " << dNeigh << ", " << endl;
126 
127  // apply Birks effect
128  Edep_B = BirksAttenuation(Edep[s],dx[s],charge[s],birks_constant);
129 
130  //cout << "\t Birks Effect: " << " Edep=" << Edep[s] << " Dx=" << dx[s]
131  // << " PID =" << pids[s] << " charge =" << charge[s] << " Edep_B=" << Edep_B << endl;
132 
133  // Calculate attenuated energy which will reach both PMTs:
134  e_dir = (Edep_B/2) * exp(-dDir/att_length - Lg[layer-1]/att_length_lg) * light_coll;
135  e_neigh = (Edep_B/2) * exp(-dNeigh/att_length - Lg[layer-1]/att_length_lg) * uturn[layer-1] * light_coll;
136 
137  // Integrate energy over entire hit. These values are the output and will be digitised:
138  etotD = etotD + e_dir;
139  etotN = etotN + e_neigh;
140 
141  // cout << "step: " << s << " etotD, etotN: " << etotD << ", " << etotN << endl;
142 
143 
144  /****** Time of hit calculation *******/
145  // In all the methods below, the assumption is that the time taken to travel along the light-guides,
146  // through PMTs and the electronics to the ADC units is the same for all paddles, therefore this time offset is ignored.
147 
148  // This takes average time of all the steps with energy deposit above 0:
149  //if (Edep[s] > 0.){
150  // timeD = timeD + (times[s] + dDir/veff) / nsteps;
151  // timeN = timeN + (times[s] + dNeigh/veff + t_u[layer-1]) / nsteps;
152  //}
153 
154  // This takes the time of the first step (in order of creation) with energy deposit above 0:
155  // if (flag_counted == 0 && Edep[s] > 0.){
156  // timeD = times[s] + dDir/veff;
157  // timeN = times[s] + dNeigh/veff + t_u[layer-1];
158  // flag_counted = 1; // so that subsequent steps are not counted in the hit
159  // }
160 
161  // This calculates the total energy * time value at edges of both paddles,
162  // will be used to get the energy-weighted average time of hit (should correspond roughly to the peak energy deposit):
163  et_D = et_D + ((times[s] + dDir/veff) * e_dir);
164  et_N = et_N + ((times[s] + dNeigh/veff + t_u[layer-1]) * e_neigh);
165 
166  } // close loop over steps s
167 
168 
169 
170  /**** The following calculates the time based on energy-weighted average of all step times ****/
171 
172  timeD = et_D / etotD; // sum(energy*time) / sum(energy)
173  timeN = et_N / etotN;
174 
175 
176  /******** end timing determination ***********/
177 
178  // cout << "Reconstructed time (ns): " << (timeD + timeN)/2. - 2.*length/veff - t_u[layer-1]/2. << endl;
179  // cout << "Reconstructed z (cm, wrt paddle center): " << (length + t_u[layer-1]*veff/2. - (timeN - timeD)*veff/2.)/10. << endl;
180  // cout << "etotD, etotN (in MeV): " << etotD << ", " << etotN << endl;
181  // cout << "timeD, timeN (in ns): " << timeD << ", " << timeN << endl;
182 
183  // Check the full output:
184  // cout << "Total steps in this hit: " << nsteps << endl;
185  // for (int s=0; s<nsteps; s++)
186  // {
187  // cout << "\n Edep (in MeV): " << Edep[s] << " time (in ns): " << times[s] << " Lpos-x (in mm): "
188  // << Lpos[s].x() << " Lpos-y: " << Lpos[s].y() << " Lpos-z: " << Lpos[s].z() << " pos-x (in mm): "
189  // << pos[s].x() << " pos-y: " << pos[s].y() << " pos-z: " << pos[s].z() << " etotD (in MeV): "
190  // << etotD << " etotN: " << etotN << " timeD (in ns): " << timeD << " timeN: " << timeN << endl;
191  // }
192 
193 
194  /**** Actual digitisation happens here! *****/
195 
196  if (etotD > 0.)
197  {
198  TDCD = (int) ((timeD + G4RandGauss::shoot(0.,sigmaTD/sqrt(etotD))) * tdc_conv);
199  ADCD = (int) (G4Poisson(etotD*light_yield*sensor_qe)*signal_split*sensor_gain*adc_conv + adc_ped);
200  }
201  if (etotN > 0.)
202  {
203  TDCN = (int) ((timeN + G4RandGauss::shoot(0.,sigmaTN/sqrt(etotN))) * tdc_conv);
204  ADCN = (int) (G4Poisson(etotN*light_yield*sensor_qe)*signal_split*sensor_gain*adc_conv + adc_ped);
205  }
206 
207  if(TDCD < 0) TDCD = 0;
208  else if (TDCD > 4096) TDCD = 4096;
209  if(TDCN < 0) TDCN = 0;
210  else if(TDCN > 4096) TDCN = 4096;
211 
212  if(ADCD < 0) ADCD = 0;
213  if(ADCN < 0) ADCN = 0;
214 
215  } // closes tInfos.eTot>0
216 
217 
218  if(verbosity>4)
219  {
220  cout << hd_msg << " layer: " << layer << ", paddle: " << paddle << " x=" << tInfos.x/cm << "cm, y=" << tInfos.y/cm << "cm, z=" << tInfos.z/cm << "cm" << endl;
221  cout << hd_msg << " Etot=" << tInfos.eTot/MeV << "MeV, average time=" << tInfos.time << "ns" << endl;
222  cout << hd_msg << " TDCD= " << TDCD << ", TDCN= " << TDCN << ", ADCD= " << ADCD << ", ADCN= " << ADCN << endl;
223  }
224 
225 
226  dgtz["hitn"] = hitn;
227  dgtz["layer"] = layer;
228  dgtz["paddle"] = paddle;
229  dgtz["ADCD"] = ADCD;
230  dgtz["ADCN"] = ADCN;
231  dgtz["TDCD"] = TDCD;
232  dgtz["TDCN"] = TDCN;
233 
234  return dgtz;
235 }
236 
237 
238 vector<identifier> cnd_HitProcess :: processID(vector<identifier> id, G4Step *step, detector Detector)
239 {
240  id[id.size()-1].id_sharing = 1;
241  return id;
242 }
243 
244 
245 double cnd_HitProcess::BirksAttenuation(double destep, double stepl, int charge, double birks)
246 {
247  //Example of Birk attenuation law in organic scintillators.
248  //adapted from Geant3 PHYS337. See MIN 80 (1970) 239-244
249  //
250  // Taken from GEANT4 examples advanced/amsEcal and extended/electromagnetic/TestEm3
251  //
252  double response = destep;
253  if (birks*destep*stepl*charge != 0.)
254  {
255  response = destep/(1. + birks*destep/stepl);
256  }
257  return response;
258 }
259 
260 
261 map< string, vector <int> > cnd_HitProcess :: multiDgt(MHit* aHit, int hitn)
262 {
263  map< string, vector <int> > MH;
264 
265  return MH;
266 }
267 
268 
269 
270 
271 
map< string, double > integrateDgt(MHit *, int)
G4LogicalVolume * GetLogical()
Returns Logical Volume pointer.
Definition: detector.h:113
map< string, vector< int > > multiDgt(MHit *, int)
double verbosity
vector< identifier > GetId()
Definition: Hit.h:103
double BirksAttenuation(double, double, int, double)
double time
Definition: HitProcess.h:43
double z
Definition: HitProcess.h:41
vector< G4ThreeVector > GetLPos()
Definition: Hit.h:76
double eTot
Definition: HitProcess.h:40
double x
Definition: HitProcess.h:41
Definition: Hit.h:22
vector< double > GetTime()
Definition: Hit.h:89
double y
Definition: HitProcess.h:41
vector< double > dimensions
vector of dimensions. Size, units depends on solid type
Definition: detector.h:77
vector< identifier > processID(vector< identifier >, G4Step *, detector)
vector< double > GetEdep()
Definition: Hit.h:83
detector GetDetector()
Definition: Hit.h:108
vector< int > GetCharges()
Definition: Hit.h:116
vector< double > GetDx()
Definition: Hit.h:86