00001 #include "SimPmtM64Oxford.h"
00002 #include "SimPmtM64CrosstalkTable.h"
00003 #include "SimPmtMaker.h"
00004 #include "SimPixelTimeBucket.h"
00005
00006 #include "TMath.h"
00007
00008 #include "MessageService/MsgService.h"
00009
00010 #include <iostream>
00011 #include <cassert>
00012 using std::cout;
00013 using std::endl;
00014
00015 CVSID("$Id: SimPmtM64Oxford.cxx,v 1.12 2007/01/15 20:12:15 rhatcher Exp $");
00016
00017
00018 SimPmtMakerProxy<SimPmtM64Oxford> gSimPmtM64OxfordProxy("SimPmtM64Oxford");
00019
00020 ClassImp(SimPmtM64Oxford)
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00035
00036
00037
00038 SimPmtM64Oxford::SimPmtM64Oxford(PlexPixelSpotId tube,
00039 VldContext context,
00040 TRandom* random ):
00041 SimPmtM64Full(tube,context,random)
00042 {
00043
00044
00045
00046 fOneSpot = true;
00047
00048 fStagesBuilt = false;
00049
00050 if (fNSpots != 1) {
00051 fOneSpot = false;
00052 MSG("DetSim",Msg::kWarning) << "This PMT has " << fNSpots
00053 << " spots! Ready for a rough ride?"
00054 << endl;
00055 }
00056
00057
00058
00059 if (fNPixels > 64) {
00060
00061 MSG("DetSim",Msg::kFatal) << "Too many pixels! /n"
00062 << "Cant continue without memory carnage!"
00063 << endl;
00064 assert(0);
00065 }
00066
00067
00068 Reset(context);
00069
00070 }
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083 Bool_t SimPmtM64Oxford::fsPmtDoChargeSmear = true;
00084
00085 const Double_t SimPmtM64Oxford::gkDefaultStageGains[13] = {0, 6.5397, 4.5583, 4.5583,
00086 2.4593, 2.4593, 2.4593,
00087 2.4593, 2.4593, 2.4593,
00088 2.4593, 2.4593, 4.5583};
00089
00090
00091 const Double_t SimPmtM64Oxford::gkDefaultSecEmmRatio = 4.5;
00092
00093
00094 const Double_t SimPmtM64Oxford::gkNLWindowBefore = 1.8056 * Munits::ns;
00095 const Double_t SimPmtM64Oxford::gkNLWindowAfter = 0.9841 * Munits::ns;
00096
00097
00098
00099
00100
00101 Double_t SimPmtM64Oxford::fsDynodeSkipRate = 0.00;
00102 Double_t SimPmtM64Oxford::fsNLThreshold = 5;
00103 Double_t SimPmtM64Oxford::fsNonlinearityScale = 1.5;
00104
00105
00106
00107
00108 void SimPmtM64Oxford::Reset(const VldContext& context)
00109 {
00110
00111 SimPmtM64::Reset(context);
00112
00113
00114 if((!fStagesBuilt)||fsRebuildGainMap) {
00115 CalStageGains();
00116 fStagesBuilt = true;
00117 }
00118 }
00119
00120
00121
00122 Bool_t SimPmtM64Oxford::CalStageGains()
00123 {
00124 Bool_t success = false;
00125
00126
00127 Double_t DefaultTubeGain = 1;
00128 for (Int_t i = 1; i <= 12; i++) {
00129 DefaultTubeGain *= gkDefaultStageGains[i];
00130 }
00131
00132
00133 for (Int_t pix = 1 ; pix <= fNPixels ; pix++){
00134
00135
00136 for (Int_t i = 0; i <= 12; i++) {
00137 fStageGains[pix][i] = gkDefaultStageGains[i];
00138 }
00139
00140
00141 Double_t thisPixelGain = 0;
00142 Double_t thisPixelWidth = 0;
00143
00144 if ( fOneSpot ) {
00145
00146 success = GetGainAndWidth(pix, 1, thisPixelGain, thisPixelWidth);
00147
00148
00149
00150 }
00151
00152 else{
00153
00154
00155 Double_t tempGain = 0;
00156 Double_t tempWidth = 0;
00157 Int_t nActiveSpots = 0;
00158
00159 for (Int_t spotit = 1; spotit <= fNSpots; spotit++){
00160 GetGainAndWidth(pix, spotit, tempGain, tempWidth);
00161 if (( tempGain > 0 ) && ( tempWidth > 0 )){
00162 thisPixelGain += tempGain;
00163 thisPixelWidth += tempWidth;
00164 nActiveSpots++;
00165 }
00166 }
00167
00168 thisPixelGain /= nActiveSpots;
00169 thisPixelWidth /= nActiveSpots;
00170
00171 }
00172
00173
00174 if (thisPixelGain > 3e6){
00175 MSG("DetSim",Msg::kInfo ) << "Huge gain in DB: " << thisPixelGain
00176 << "\tTruncating to 3e6" << endl;
00177 thisPixelGain = 3e6;
00178 }
00179
00180
00181
00182
00183
00184
00185
00186 Double_t secEmmCrtn = 1;
00187 Double_t effSecEmmRatio = 1/( thisPixelWidth*thisPixelWidth );
00188
00189
00190
00191
00192
00193
00194
00195 secEmmCrtn = effSecEmmRatio * ( 1. + sqrt( 1. + 1./effSecEmmRatio ) );
00196 secEmmCrtn /= gkDefaultSecEmmRatio * (1. + sqrt(1.+1./gkDefaultSecEmmRatio ) );
00197
00198
00199 fStageGains[pix][1] *= secEmmCrtn;
00200
00201
00202
00203 secEmmCrtn = pow(secEmmCrtn, 1./11.);
00204
00205
00206
00207 for(Int_t i = 2; i <= 12; i++){
00208 fStageGains[pix][i] /= secEmmCrtn;
00209 }
00210
00211
00212
00213
00214
00215 const Double_t beta = 0.890199;
00216
00217
00218
00219
00220 fSkippingGain[pix] = pow( fStageGains[pix][1], 1./ beta )
00221 + pow( fStageGains[pix][2], 1./ beta );
00222
00223
00224
00225 fSkippingGain[pix] = pow( fSkippingGain[pix], beta );
00226
00227
00228
00229
00230
00231
00232 Double_t gainRatio = 0;
00233 gainRatio = thisPixelGain / DefaultTubeGain ;
00234
00235
00236 gainRatio = pow(gainRatio, 1./12.);
00237
00238
00239
00240 for (Int_t i = 0; i <= 12; i++) {
00241 fStageGains[pix][i] *= gainRatio;
00242 }
00243
00244
00245
00246 fSkippingGain[pix] = fSkippingGain[pix] * gainRatio * gainRatio;
00247
00248 }
00249
00250 return success;
00251 }
00252
00253
00254
00255 Float_t SimPmtM64Oxford::GenChargeFromPE( Int_t pixel,
00256 Int_t spot, Float_t pe )
00257 {
00258 if (spot != 1) {
00259 MSG("DetSim",Msg::kError) << "Amplifiying a pe in spot "<< spot
00260 << "! This is okay... /n"
00261 << "But Non-linearity will fail"
00262 << endl;
00263 }
00264
00265 if (pe <= 0) return 0;
00266
00267 Int_t nPe = (Int_t) pe;
00268
00269 if (nPe > 1200){
00270
00271 MSG("DetSim",Msg::kInfo ) << "BIG pulse seen: " << nPe
00272 << " photoelectons."
00273 << "\tTruncating to 1200" << endl;
00274 nPe = 1200;
00275 }
00276
00277
00278
00279
00280
00281 UInt_t neFromD2 = SimFirstDynodes(pixel, nPe);
00282 UInt_t neToAnode = SimLaterDynodes(pixel, neFromD2);
00283
00284 return neToAnode * Munits::e_SI;
00285
00286 }
00287
00288
00289
00290
00291 UInt_t SimPmtM64Oxford::SimFirstDynodes(Int_t pixel, Int_t nPe)
00292 {
00293
00294
00295
00296
00297
00298 Float_t meanSkipping = nPe * fsDynodeSkipRate;
00299 UInt_t neThatSkip =
00300 fRandom->Poisson( meanSkipping * fSkippingGain[pixel] );
00301
00302
00303 Float_t meanElectrons = 6.5;
00304 UInt_t neFromPrevDynode = nPe ;
00305
00306
00307 meanElectrons = fStageGains[pixel][1] * nPe * (1 - fsDynodeSkipRate);
00308 neFromPrevDynode = (UInt_t) fRandom->PoissonD(meanElectrons);
00309
00310
00311 meanElectrons = fStageGains[pixel][2] * neFromPrevDynode;
00312 neFromPrevDynode = (UInt_t) fRandom->PoissonD(meanElectrons);
00313
00314
00315 neFromPrevDynode += neThatSkip;
00316
00317 return neFromPrevDynode;
00318 }
00319
00320
00321
00322
00323 UInt_t SimPmtM64Oxford::SimLaterDynodes(Int_t pixel, Int_t neToD3)
00324 {
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371 Float_t meanElectrons = 29.3;
00372 UInt_t neFromPrevDynode = neToD3;
00373 for (Int_t i = 3; i <= 12; i++) {
00374 meanElectrons = fStageGains[pixel][i] * (Float_t) neFromPrevDynode;
00375 neFromPrevDynode = (UInt_t) fRandom->PoissonD(meanElectrons);
00376 }
00377
00378 return neFromPrevDynode;
00379 }
00380
00381
00382 void SimPmtM64Oxford::SimulateAnodeEffects()
00383 {
00384
00385
00386 Int_t activeSpot = 1;
00387
00388 if (!fOneSpot) {
00389 MSG("DetSim",Msg::kError ) << "This tube has" << fNSpots
00390 << " spots active! Only PEs in spot "
00391 << activeSpot << " will be considered."
00392 << endl;
00393 }
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404 typedef std::vector<PeNLDigest_t> AllPePixelList_t;
00405
00406 std::vector<AllPePixelList_t> allPeList(fNPixels + 1);
00407
00408
00409
00410
00411
00412 for(SimPmtBucketIterator it(*this); !it.End(); it.Next()) {
00413 Int_t bucketId = it.BucketId();
00414 SimPmtTimeBucket& pmtBucket = it.Bucket();
00415
00416
00417 for(Int_t pix = 1 ; pix <= fNPixels ; pix++) {
00418 SimPixelTimeBucket& pixelBucket = pmtBucket.GetPixelBucket(pix);
00419 Float_t q = 0.;
00420 if ( pixelBucket.GetTotalPEXtalk() ) {
00421 q = pixelBucket.GetCharge();
00422 q /= pixelBucket.GetTotalPEXtalk();
00423 }
00424
00425
00426 SimPixelTimeBucket::PeList_t& peInThisBkt
00427 = pixelBucket.GetDigiPEXtalk(activeSpot);
00428
00429
00430
00431 SimPixelTimeBucket::PeList_t::iterator peItr
00432 = peInThisBkt.begin();
00433
00434 for ( ; peItr != peInThisBkt.end() ; peItr++ ) {
00435
00436
00437 PeNLDigest_t peInfo(bucketId, peItr->first, q);
00438
00439
00440
00441 allPeList[pix].push_back(peInfo);
00442
00443 }
00444 }
00445 }
00446
00447 MSG("DetSim",Msg::kVerbose) << "Constructed pe list for non-linearity"
00448 << endl;
00449
00450
00451
00452
00453
00454
00455 for(Int_t pix = 1 ; pix <= fNPixels ; pix++) {
00456
00457
00458 AllPePixelList_t::iterator currPeItr = allPeList[pix].begin();
00459 for ( ; currPeItr != allPeList[pix].end() ; ++currPeItr) {
00460
00461
00462
00463 Int_t bucketId = currPeItr->fBucketNo;
00464 SimPmtTimeBucket& pmtBkt = GetBucket(bucketId);
00465 SimPixelTimeBucket& pixelBucket = pmtBkt.GetPixelBucket(pix);
00466
00467 Double_t chargeRatio = 1;
00468
00469
00470 if (fsPmtDoNonlinearity){
00471
00472
00473 Float_t peTime = currPeItr->fTime;
00474 Float_t nearbyCharge = 0;
00475 AllPePixelList_t::iterator nearbyPeItr = currPeItr;
00476 Float_t dt = 0;
00477
00478
00479 while ( nearbyPeItr != allPeList[pix].begin() ) {
00480 --nearbyPeItr;
00481 dt = peTime - nearbyPeItr->fTime;
00482 if ( dt > gkNLWindowBefore ) break;
00483 nearbyCharge += nearbyPeItr->fCharge;
00484
00485 }
00486
00487
00488 nearbyPeItr = currPeItr;
00489
00490 while (nearbyPeItr != allPeList[pix].end() ) {
00491 dt = nearbyPeItr->fTime - peTime;
00492 if ( dt > gkNLWindowAfter ) break;
00493 nearbyCharge += nearbyPeItr->fCharge;
00494 ++nearbyPeItr;
00495 }
00496
00497
00498
00499
00500
00501 if (nearbyCharge){
00502
00503
00504 chargeRatio = GenNonlinearCharge( pix, nearbyCharge );
00505 chargeRatio /= nearbyCharge;
00506 }
00507
00508
00509 Float_t deltaq = (chargeRatio - 1) * currPeItr->fCharge;
00510 pixelBucket.AddCharge(deltaq);
00511
00512 }
00513
00514
00515
00516 if (fsPmtDoChargeSmear){
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526 Float_t startTime = BucketToStartTime(bucketId);
00527 Float_t stopTime = BucketToStopTime(bucketId);
00528
00529 SimPmtTimeBucket& nextPmtBucket = GetBucket(bucketId + 1);
00530 SimPixelTimeBucket& nextPixBucket = nextPmtBucket.GetPixelBucket(pix);
00531 SimPmtTimeBucket& prevPmtBucket = GetBucket(bucketId - 1);
00532 SimPixelTimeBucket& prevPixBucket = prevPmtBucket.GetPixelBucket(pix);
00533
00534
00535 Float_t qNext = currPeItr->fCharge * chargeRatio;
00536 qNext *= NextBucketFraction ( stopTime - currPeItr->fTime );
00537 Float_t qPrev = currPeItr->fCharge * chargeRatio;
00538 qPrev *= PrevBucketFraction ( currPeItr->fTime - startTime );
00539
00540
00541
00542
00543
00544 nextPixBucket.AddCharge(qNext);
00545 if ( qNext > 1.0 * Munits::fC ) {
00546 nextPixBucket.SetTruthBit(DigiSignal::kLeakFromPrevBucket);
00547 }
00548
00549
00550
00551 prevPixBucket.AddCharge(qPrev);
00552 if ( qPrev > 1.0 * Munits::fC ) {
00553 prevPixBucket.SetTruthBit(DigiSignal::kLeakFromNextBucket);
00554 }
00555
00556
00557 pixelBucket.AddCharge( -(qPrev + qNext) );
00558
00559 }
00560 }
00561 }
00562
00563 MSG("Detsim",Msg::kVerbose) << "Nonlinearity and Charge smearing done"
00564 << endl;
00565
00566 }
00567
00568
00569 Float_t SimPmtM64Oxford::GenNonlinearCharge( Int_t ,
00570 Float_t linCharge)
00571 {
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586 Float_t scaleNL = 1.;
00587 const Float_t qInPe = linCharge / (1e6 * Munits::e_SI);
00588
00589 const Float_t dGdQ = 3.9e-3;
00590
00591
00592 if (qInPe > fsNLThreshold) {
00593 const Float_t alpha = 2 * dGdQ * fsNonlinearityScale;
00594
00595
00596 scaleNL = 1 - TMath::Exp( -alpha * (qInPe - fsNLThreshold) );
00597 scaleNL /= alpha * ( qInPe - fsNLThreshold );
00598
00599 }
00600
00601 return linCharge * scaleNL;
00602
00603 }
00604
00605
00606 void SimPmtM64Oxford::SimulateCharges()
00607 {
00608 fTotalCharge =0;
00609
00610
00611
00612
00613 for(SimPmtBucketIterator it(*this); !it.End(); it.Next()) {
00614 SimPmtTimeBucket& pmtBucket = it.Bucket();
00615
00616
00617 for(Int_t pix = 1 ; pix <= fNPixels ; pix++) {
00618 SimPixelTimeBucket& pixelBucket = pmtBucket.GetPixelBucket(pix);
00619 Float_t nPe = pixelBucket.GetTotalPEXtalk();
00620
00621
00622
00623 for (Int_t spot = 1 ; spot <= fNSpots ; spot++){
00624 Float_t q = GenChargeFromPE( pix, spot, nPe );
00625
00626 pixelBucket.AddCharge(q);
00627 fTotalCharge += q;
00628 }
00629 }
00630 }
00631 }
00632
00633
00634
00635 void SimPmtM64Oxford::Config( Registry& config )
00636 {
00637 double dtmp;
00638 int itmp;
00639 if(config.Get("pmtDoChargeSmear",itmp)) fsPmtDoChargeSmear = itmp;
00640 if(config.Get("pmtM64DynodeSkipRate",dtmp)) fsDynodeSkipRate = dtmp;
00641 if(config.Get("pmtM64NLThreshold",dtmp)) fsNLThreshold = dtmp;
00642 if(config.Get("pmtM64NonlinearityScale",dtmp)) fsNonlinearityScale =dtmp;
00643
00644 SimPmtM64Full::Config(config);
00645
00646
00647
00648
00649 if (0 == fsNonlinearityScale) fsPmtDoNonlinearity = 0;
00650 }
00651
00652
00653
00654 void SimPmtM64Oxford::SimulatePmt()
00655 {
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665 if(fsPmtDoOpticalCrosstalk) SimulateOpticalXtalk();
00666 else CopyPEtoPEXtalk();
00667
00668
00669
00670
00671
00672
00673 SimulateCharges();
00674
00675
00676
00677
00678
00679
00680 if(fsPmtDoDarkNoise) SimulateDarkNoise();
00681 if(fsPmtDoChargeSmear ||
00682 fsPmtDoNonlinearity ) SimulateAnodeEffects();
00683 if(fsPmtDoChargeCrosstalk) SimulateChargeCrosstalk();
00684
00685
00686
00687
00688 }