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

Last change on this file since 17873 was 17643, checked in by ftemme, 11 years ago
Added a MParameterD for the Crosstalkcoefficient, so that MSimCamera can access the crosstalk coefficient for the calculation of the accoupling.
File size: 13.8 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 default:
229 *fLog << err << "ERROR - APD type " << fType << " undefined." << endl;
230 return kFALSE;
231 }
232
233 for (UInt_t i=0; i<fGeom->GetNumPixels(); i++)
234 {
235 APD *apd = new APD(ncells, crosstalk, deadtime, recovery);
236 apd->SetAfterpulseProb(afterprob1, afterprob2);
237
238 fAPDs.Add(apd);
239 }
240
241 return kTRUE;
242}
243
244// --------------------------------------------------------------------------
245//
246// Process all photons through the corresponding APD and set the output
247// (weight) accordingly.
248//
249Int_t MSimAPD::Process()
250{
251 // Make all APDs look neutral for the first hit by a photon according to the
252 // average hit rate
253 const UInt_t npix = fAPDs.GetEntriesFast();
254
255 // Check if we can safely proceed (this can fail if we either haven't been
256 // ReInit'ed or the max index in MPhotonStatistics is wrong)
257 if ((Int_t)npix<fStat->GetMaxIndex())
258 {
259 *fLog << err << "ERROR - MSimAPD::Process: Only " << npix << " APDs initialized. At least " << fStat->GetMaxIndex() << " needed... abort." << endl;
260 return kERROR;
261 }
262
263 // To ensure that the photons are always sorted when calling
264 // HitRandomCellRelative the array is sorted here. If it is sorted
265 // already nothing will be done since the status is stored.
266 // FIXME: Check that this is true and check that it is really necessary
267 fEvt->Sort();
268
269 // This tries to initialize dead and relaxing cells properly. If
270 // the APD has not been initialized before the chip is randomsly
271 // filled, otherwise a time window of the default relaxing time
272 // is simulated, so that the previous influence is less than a permille.
273 for (UInt_t idx=0; idx<npix; idx++)
274 {
275 const Double_t freq = fRates ? (*fRates)[idx].GetPedestal() : fFreq;
276
277 // Init creates an empty G-APD, i.e. without external pulses
278 // but the correct history for afterpulses and relaxation.
279 // If it was already initialized once it evolves the G-APD
280 // for a time until the effect of relaxation and afterpulses
281 // is below 0.1%. The also creates the possible afterpulses
282 // of the future and deletes later afterpulses from the list.
283 // After the the time stamp fTime is set to 0.
284 static_cast<APD*>(fAPDs.UncheckedAt(idx))->Init(freq);
285 }
286
287 // Get number of photons
288 const Int_t num = fEvt->GetNumPhotons();
289
290 // Loop over all photons
291 for (Int_t i=0; i<num; i++)
292 {
293 // Get i-th photon
294 MPhotonData &ph = (*fEvt)[i];
295
296 // Get arrival time of photon wrt to left edge of window and its index
297 const Double_t t = ph.GetTime()-fStat->GetTimeFirst();
298 const Int_t idx = ph.GetTag();
299 if (idx<0)
300 {
301 *fLog << err << "ERROR - MSimAPD: Invalid index -1." << endl;
302 return kERROR;
303 }
304
305 if (ph.GetWeight()!=1)
306 {
307 *fLog << err << "ERROR - MSimAPD: Weight of " << i << "-th photon not 1, but " << ph.GetWeight() << endl;
308 ph.Print();
309 return kERROR;
310 }
311
312 // Simulate hitting the APD at a time t after T0 (APD::fTime).
313 // Crosstalk is taken into account and the resulting signal height
314 // in effective "number of photons" is returned. Afterpulses until
315 // this time "hit" the G-APD and newly created afterpulses
316 // are stored in the list of afterpulses
317 const Double_t hits = static_cast<APD*>(fAPDs.UncheckedAt(idx))->HitRandomCellRelative(t);
318
319 // Set the weight to the input
320 ph.SetWeight(hits);
321 }
322
323 // Now we have to shift the evolved time of all APDs to the end of our
324 // simulated time.
325 for (UInt_t idx=0; idx<npix; idx++)
326 {
327 APD *a = static_cast<APD*>(fAPDs.UncheckedAt(idx));
328
329 const Double_t end = fStat->GetTimeLast()-fStat->GetTimeFirst();
330
331 // This moves T0 (APD::fTime) at the right edge of our time-
332 // window. For the next event this means that afterpulses of past
333 // noise and afterpulses will be available already.
334 // FIXME: Note, that this might mean that a cosmics-pulse
335 // might increase the noise above the normal level.
336 a->IncreaseTime(end);
337
338 // Get the afterpulses and add them to the signal
339 TIter Next(&a->GetListOfAfterpulses());
340 Afterpulse *ap = 0;
341 while ((ap=static_cast<Afterpulse*>(Next())))
342 {
343 // Skip afterpulses later than that which have been
344 // already produced
345 if (ap->GetTime()>=end)
346 continue;
347
348 // Add a new photon
349 // FIXME: SLOW!
350 MPhotonData &ph = fEvt->Add();
351
352 // Set source to Artificial (noise), the amplitude produced by the
353 // afterpulse (includes crosstalk), its arrival time
354 // and its amplitude, as well as the index of the channel it
355 // corresponds to.
356 ph.SetPrimary(MMcEvtBasic::kArtificial);
357 ph.SetWeight(ap->GetAmplitude());
358 ph.SetTime(ap->GetTime()+fStat->GetTimeFirst());
359 ph.SetTag(idx);
360 }
361
362 // It seems to make sense to delete the previous afterpulses now
363 // but this is not necessary. We have to loop over them in any
364 // case. So we omit the additional loop for deletion but instead
365 // do the deletion in the next loop at the end of Init()
366 // If needed this could be used to keep the total memory
367 // consumption slighly lower.
368 }
369
370 // Now the newly added afterpulses have to be sorted into the array correctly
371 fEvt->Sort();
372
373 return kTRUE;
374}
375
376// --------------------------------------------------------------------------
377//
378// NameGeomCam
379// Type: 1
380//
381// Example for a custom made G-APD:
382//
383// Type: 0
384// NumCells: 60
385// CrosstalkCoefficient: 0.1 // [prob]
386// DeadTime: 3 // [ns]
387// RecoveryTime: 8.75 // [ns]
388// AfterpulseProb1: 0.14 // [prob]
389// AfterpulseProb2: 0.11 // [prob]
390//
391Int_t MSimAPD::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
392{
393 Bool_t rc = kFALSE;
394 if (IsEnvDefined(env, prefix, "NameGeomCam", print))
395 {
396 rc = kTRUE;
397 fNameGeomCam = GetEnvValue(env, prefix, "NameGeomCam", fNameGeomCam);
398 }
399
400 if (IsEnvDefined(env, prefix, "Type", print))
401 {
402 rc = kTRUE;
403 fType = GetEnvValue(env, prefix, "Type", fType);
404 }
405
406 if (fType!=0)
407 return rc;
408
409 if (IsEnvDefined(env, prefix, "NumCells", print))
410 {
411 rc = kTRUE;
412 fNumCells = GetEnvValue(env, prefix, "NumCells", fNumCells);
413 }
414 if (IsEnvDefined(env, prefix, "CrosstalkCoefficient", print))
415 {
416 rc = kTRUE;
417 fCrosstalkCoeff = GetEnvValue(env, prefix, "CrosstalkCoefficient", fCrosstalkCoeff);
418 }
419 if (IsEnvDefined(env, prefix, "DeadTime", print))
420 {
421 rc = kTRUE;
422 fDeadTime = GetEnvValue(env, prefix, "DeadTime", fDeadTime);
423 }
424 if (IsEnvDefined(env, prefix, "RecoveryTime", print))
425 {
426 rc = kTRUE;
427 fRecoveryTime = GetEnvValue(env, prefix, "RecoveryTime", fRecoveryTime);
428 }
429 if (IsEnvDefined(env, prefix, "AfterpulseProb1", print))
430 {
431 rc = kTRUE;
432 fAfterpulseProb1 = GetEnvValue(env, prefix, "AfterpulseProb1", fAfterpulseProb1);
433 }
434 if (IsEnvDefined(env, prefix, "AfterpulseProb2", print))
435 {
436 rc = kTRUE;
437 fAfterpulseProb2 = GetEnvValue(env, prefix, "AfterpulseProb2", fAfterpulseProb2);
438 }
439
440 return rc;
441}
Note: See TracBrowser for help on using the repository browser.