source: trunk/Mars/msimcamera/MSimAPD.cc@ 19696

Last change on this file since 19696 was 19548, checked in by tbretz, 6 years ago
Added new G-APD type (SensL FJ 6x6, preliminary\!)
File size: 14.0 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of CheObs, the Modular Analysis and Reconstruction
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appears in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without express
14! * or implied warranty.
15! *
16!
17!
18! Author(s): Thomas Bretz, 1/2009 <mailto:tbretz@astro.uni-wuerzburg.de>
19!
20! Copyright: CheObs Software Development, 2000-2009
21!
22!
23\* ======================================================================== */
24
25//////////////////////////////////////////////////////////////////////////////
26//
27// MSimAPD
28//
29// This tasks simulates the individual APDs. Before starting the APD is
30// initialized randomly according to the photon rate hitting the APD. Such
31// it is assumed that the initial condition of the APD is similar to the real
32// one. In this context it is assumed that the events are independent, so
33// that the APD is always in the same condition.
34//
35// For every photon and event the behaviour of the APD is simulated. The
36// output is set as weight to the MPhotonData containers.
37//
38// Remark:
39// - The photon rate used to initialize the APD must match the one used
40// to "fill" the random photons. (FIXME: This should be stored somewhere)
41//
42// Input Containers:
43// fNameGeomCam [MGeomCam]
44// MPhotonEvent
45// MPhotonStatistics
46//
47// Output Containers:
48// MPhotonEvent
49//
50//////////////////////////////////////////////////////////////////////////////
51#include "MSimAPD.h"
52
53#include <TH2.h>
54#include <TRandom.h>
55
56#include "MLog.h"
57#include "MLogManip.h"
58
59#include "MMath.h"
60#include "MParList.h"
61
62#include "MGeomCam.h"
63
64#include "MPhotonEvent.h"
65#include "MPhotonData.h"
66
67#include "MPedestalCam.h"
68#include "MPedestalPix.h"
69
70#include "MAvalanchePhotoDiode.h"
71
72#include "MParameters.h"
73
74ClassImp(MSimAPD);
75
76using namespace std;
77
78// --------------------------------------------------------------------------
79//
80// Default Constructor.
81//
82MSimAPD::MSimAPD(const char* name, const char *title)
83 : fGeom(0), fEvt(0), fStat(0), fType(1),
84 fNumCells(60), fCrosstalkCoeff(0), fDeadTime(3),
85 fRecoveryTime(8.75), fAfterpulseProb1(0.11), fAfterpulseProb2(0.14)
86
87{
88 fName = name ? name : "MSimAPD";
89 fTitle = title ? title : " Task to simulate the detection behaviour of APDs";
90}
91
92// --------------------------------------------------------------------------
93//
94// Get the necessary parameter containers
95//
96Int_t MSimAPD::PreProcess(MParList *pList)
97{
98 if (fNameGeomCam.IsNull())
99 {
100 *fLog << inf << "No geometry container... skipping." << endl;
101 return kSKIP;
102 }
103
104 fGeom = (MGeomCam*)pList->FindObject(fNameGeomCam, "MGeomCam");
105 if (!fGeom)
106 {
107 *fLog << inf << fNameGeomCam << " [MGeomCam] not found..." << endl;
108
109 fGeom = (MGeomCam*)pList->FindObject("MGeomCam");
110 if (!fGeom)
111 {
112 *fLog << err << "MGeomCam not found... aborting." << endl;
113 return kFALSE;
114 }
115 }
116
117 fStat = (MPhotonStatistics*)pList->FindObject("MPhotonStatistics");
118 if (!fStat)
119 {
120 *fLog << err << "MPhotonStatistics not found... aborting." << endl;
121 return kFALSE;
122 }
123
124 fEvt = (MPhotonEvent*)pList->FindObject("MPhotonEvent");
125 if (!fEvt)
126 {
127 *fLog << err << "MPhotonEvent not found... aborting." << endl;
128 return kFALSE;
129 }
130
131 fCrosstalkCoeffParam = (MParameterD*)pList->FindCreateObj("MParameterD","CrosstalkCoeffParam");
132 if (!fCrosstalkCoeffParam)
133 {
134 *fLog << err << "CrosstalkCoeffParam [MParameterD] not found... aborting." << endl;
135 return kFALSE;
136 }
137 fCrosstalkCoeffParam->SetVal(fCrosstalkCoeff);
138
139 fRates = (MPedestalCam*)pList->FindObject("AccidentalPhotonRates", "MPedestalCam");
140 if (!fRates)
141 {
142 *fLog << inf;
143 *fLog << "AccidentalPhotonRates [MPedestalCam] not found..." << endl;
144 *fLog << " using " << fFreq << " as default for all G-APDs." << endl;
145 }
146
147 return kTRUE;
148}
149
150// --------------------------------------------------------------------------
151//
152// Initialize as many APDs as we have pixels in the fGeomCam
153//
154Bool_t MSimAPD::ReInit(MParList *plist)
155{
156 if (UInt_t(fAPDs.GetEntriesFast())==fGeom->GetNumPixels())
157 return kTRUE;
158
159 fAPDs.Delete();
160
161 // FIXME:
162 // * initialize an empty APD and read the APD setup from a file to
163 // allow different APDs.
164 // * Make the arguments a data member of MSimAPD
165
166 Int_t ncells = 0;
167 Float_t crosstalk = 0;
168 Float_t deadtime = 0;
169 Float_t recovery = 0;
170 Float_t afterprob1 = 0;
171 Float_t afterprob2 = 0;
172
173 switch (fType)
174 {
175 case 0:
176 ncells = fNumCells;
177 crosstalk = fCrosstalkCoeff;
178 deadtime = fDeadTime;
179 recovery = fRecoveryTime;
180 afterprob1 = fAfterpulseProb1;
181 afterprob2 = fAfterpulseProb2;
182
183 gLog << inf << "Using custom G-APD parameters:" << endl;
184 gLog << " - Num cells: " << fNumCells << endl;
185 gLog << " - Crosstalk coeff.: " << fCrosstalkCoeff << endl;
186 gLog << " - Dead time: " << fDeadTime << " ns" << endl;
187 gLog << " - Recovery time: " << fRecoveryTime << " ns" << endl;
188 gLog << " - Afterpulse 1 prob.: " << fAfterpulseProb1 << endl;
189 gLog << " - Afterpulse 2 prob.: " << fAfterpulseProb2 << endl;
190 break;
191
192 case 1:
193 ncells = 30;
194 crosstalk = 0.2;
195 deadtime = 3;
196 recovery = 8.75*4;
197 afterprob1 = 0;
198 afterprob2 = 0;
199 break;
200
201 case 2:
202 ncells = 60;
203 crosstalk = 0.2;
204 deadtime = 3;
205 recovery = 8.75;
206 afterprob1 = 0;
207 afterprob2 = 0;
208 break;
209
210 case 3:
211 ncells = 60;
212 crosstalk = 0.15;
213 deadtime = 3;
214 recovery = 8.75;
215 afterprob1 = 0;
216 afterprob2 = 0;
217 break;
218
219 case 4:
220 ncells = 60;
221 crosstalk = 0.15;
222 deadtime = 3;
223 recovery = 8.75;
224 afterprob1 = 0.14;
225 afterprob2 = 0.11;
226 break;
227
228 case 5:
229 ncells = 150;
230 crosstalk = 0.20;
231 deadtime = 3;
232 recovery = 50;
233 afterprob1 = 0.03;
234 afterprob2 = 0.02;
235 break;
236
237 default:
238 *fLog << err << "ERROR - APD type " << fType << " undefined." << endl;
239 return kFALSE;
240 }
241
242 for (UInt_t i=0; i<fGeom->GetNumPixels(); i++)
243 {
244 APD *apd = new APD(ncells, crosstalk, deadtime, recovery);
245 apd->SetAfterpulseProb(afterprob1, afterprob2);
246
247 fAPDs.Add(apd);
248 }
249
250 return kTRUE;
251}
252
253// --------------------------------------------------------------------------
254//
255// Process all photons through the corresponding APD and set the output
256// (weight) accordingly.
257//
258Int_t MSimAPD::Process()
259{
260 // Make all APDs look neutral for the first hit by a photon according to the
261 // average hit rate
262 const UInt_t npix = fAPDs.GetEntriesFast();
263
264 // Check if we can safely proceed (this can fail if we either haven't been
265 // ReInit'ed or the max index in MPhotonStatistics is wrong)
266 if ((Int_t)npix<fStat->GetMaxIndex())
267 {
268 *fLog << err << "ERROR - MSimAPD::Process: Only " << npix << " APDs initialized. At least " << fStat->GetMaxIndex() << " needed... abort." << endl;
269 return kERROR;
270 }
271
272 // To ensure that the photons are always sorted when calling
273 // HitRandomCellRelative the array is sorted here. If it is sorted
274 // already nothing will be done since the status is stored.
275 // FIXME: Check that this is true and check that it is really necessary
276 fEvt->Sort();
277
278 // This tries to initialize dead and relaxing cells properly. If
279 // the APD has not been initialized before the chip is randomsly
280 // filled, otherwise a time window of the default relaxing time
281 // is simulated, so that the previous influence is less than a permille.
282 for (UInt_t idx=0; idx<npix; idx++)
283 {
284 const Double_t freq = fRates ? (*fRates)[idx].GetPedestal() : fFreq;
285
286 // Init creates an empty G-APD, i.e. without external pulses
287 // but the correct history for afterpulses and relaxation.
288 // If it was already initialized once it evolves the G-APD
289 // for a time until the effect of relaxation and afterpulses
290 // is below 0.1%. The also creates the possible afterpulses
291 // of the future and deletes later afterpulses from the list.
292 // After the the time stamp fTime is set to 0.
293 static_cast<APD*>(fAPDs.UncheckedAt(idx))->Init(freq);
294 }
295
296 // Get number of photons
297 const Int_t num = fEvt->GetNumPhotons();
298
299 // Loop over all photons
300 for (Int_t i=0; i<num; i++)
301 {
302 // Get i-th photon
303 MPhotonData &ph = (*fEvt)[i];
304
305 // Get arrival time of photon wrt to left edge of window and its index
306 const Double_t t = ph.GetTime()-fStat->GetTimeFirst();
307 const Int_t idx = ph.GetTag();
308 if (idx<0)
309 {
310 *fLog << err << "ERROR - MSimAPD: Invalid index -1." << endl;
311 return kERROR;
312 }
313
314 if (ph.GetWeight()!=1)
315 {
316 *fLog << err << "ERROR - MSimAPD: Weight of " << i << "-th photon not 1, but " << ph.GetWeight() << endl;
317 ph.Print();
318 return kERROR;
319 }
320
321 // Simulate hitting the APD at a time t after T0 (APD::fTime).
322 // Crosstalk is taken into account and the resulting signal height
323 // in effective "number of photons" is returned. Afterpulses until
324 // this time "hit" the G-APD and newly created afterpulses
325 // are stored in the list of afterpulses
326 const Double_t hits = static_cast<APD*>(fAPDs.UncheckedAt(idx))->HitRandomCellRelative(t);
327
328 // Set the weight to the input
329 ph.SetWeight(hits);
330 }
331
332 // Now we have to shift the evolved time of all APDs to the end of our
333 // simulated time.
334 for (UInt_t idx=0; idx<npix; idx++)
335 {
336 APD *a = static_cast<APD*>(fAPDs.UncheckedAt(idx));
337
338 const Double_t end = fStat->GetTimeLast()-fStat->GetTimeFirst();
339
340 // This moves T0 (APD::fTime) at the right edge of our time-
341 // window. For the next event this means that afterpulses of past
342 // noise and afterpulses will be available already.
343 // FIXME: Note, that this might mean that a cosmics-pulse
344 // might increase the noise above the normal level.
345 a->IncreaseTime(end);
346
347 // Get the afterpulses and add them to the signal
348 TIter Next(&a->GetListOfAfterpulses());
349 Afterpulse *ap = 0;
350 while ((ap=static_cast<Afterpulse*>(Next())))
351 {
352 // Skip afterpulses later than that which have been
353 // already produced
354 if (ap->GetTime()>=end)
355 continue;
356
357 // Add a new photon
358 // FIXME: SLOW!
359 MPhotonData &ph = fEvt->Add();
360
361 // Set source to Artificial (noise), the amplitude produced by the
362 // afterpulse (includes crosstalk), its arrival time
363 // and its amplitude, as well as the index of the channel it
364 // corresponds to.
365 ph.SetPrimary(MMcEvtBasic::kArtificial);
366 ph.SetWeight(ap->GetAmplitude());
367 ph.SetTime(ap->GetTime()+fStat->GetTimeFirst());
368 ph.SetTag(idx);
369 }
370
371 // It seems to make sense to delete the previous afterpulses now
372 // but this is not necessary. We have to loop over them in any
373 // case. So we omit the additional loop for deletion but instead
374 // do the deletion in the next loop at the end of Init()
375 // If needed this could be used to keep the total memory
376 // consumption slighly lower.
377 }
378
379 // Now the newly added afterpulses have to be sorted into the array correctly
380 fEvt->Sort();
381
382 return kTRUE;
383}
384
385// --------------------------------------------------------------------------
386//
387// NameGeomCam
388// Type: 1
389//
390// Example for a custom made G-APD:
391//
392// Type: 0
393// NumCells: 60
394// CrosstalkCoefficient: 0.1 // [prob]
395// DeadTime: 3 // [ns]
396// RecoveryTime: 8.75 // [ns]
397// AfterpulseProb1: 0.14 // [prob]
398// AfterpulseProb2: 0.11 // [prob]
399//
400Int_t MSimAPD::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
401{
402 Bool_t rc = kFALSE;
403 if (IsEnvDefined(env, prefix, "NameGeomCam", print))
404 {
405 rc = kTRUE;
406 fNameGeomCam = GetEnvValue(env, prefix, "NameGeomCam", fNameGeomCam);
407 }
408
409 if (IsEnvDefined(env, prefix, "Type", print))
410 {
411 rc = kTRUE;
412 fType = GetEnvValue(env, prefix, "Type", fType);
413 }
414
415 if (fType!=0)
416 return rc;
417
418 if (IsEnvDefined(env, prefix, "NumCells", print))
419 {
420 rc = kTRUE;
421 fNumCells = GetEnvValue(env, prefix, "NumCells", fNumCells);
422 }
423 if (IsEnvDefined(env, prefix, "CrosstalkCoefficient", print))
424 {
425 rc = kTRUE;
426 fCrosstalkCoeff = GetEnvValue(env, prefix, "CrosstalkCoefficient", fCrosstalkCoeff);
427 }
428 if (IsEnvDefined(env, prefix, "DeadTime", print))
429 {
430 rc = kTRUE;
431 fDeadTime = GetEnvValue(env, prefix, "DeadTime", fDeadTime);
432 }
433 if (IsEnvDefined(env, prefix, "RecoveryTime", print))
434 {
435 rc = kTRUE;
436 fRecoveryTime = GetEnvValue(env, prefix, "RecoveryTime", fRecoveryTime);
437 }
438 if (IsEnvDefined(env, prefix, "AfterpulseProb1", print))
439 {
440 rc = kTRUE;
441 fAfterpulseProb1 = GetEnvValue(env, prefix, "AfterpulseProb1", fAfterpulseProb1);
442 }
443 if (IsEnvDefined(env, prefix, "AfterpulseProb2", print))
444 {
445 rc = kTRUE;
446 fAfterpulseProb2 = GetEnvValue(env, prefix, "AfterpulseProb2", fAfterpulseProb2);
447 }
448
449 return rc;
450}
Note: See TracBrowser for help on using the repository browser.